Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
local tArgs = { ... }
local function printUsage()
print( "Usages:" )
print( "chat host <hostname>" )
print( "chat join <hostname> <nickname>" )
end
local sOpenedModem = nil
local function openModem()
for n,sModem in ipairs( peripheral.getNames() ) do
if peripheral.getType( sModem ) == "modem" then
if not rednet.isOpen( sModem ) then
rednet.open( sModem )
sOpenedModem = sModem
end
return true
end
end
print( "No modems found." )
return false
end
local function closeModem()
if sOpenedModem ~= nil then
rednet.close( sOpenedModem )
sOpenedModem = nil
end
end
-- Colours
local highlightColour, textColour
if term.isColour() then
textColour = colours.white
highlightColour = colours.yellow
else
textColour = colours.white
highlightColour = colours.white
end
local sCommand = tArgs[1]
if sCommand == "host" then
-- "chat host"
-- Get hostname
local sHostname = tArgs[2]
if sHostname == nil then
printUsage()
return
end
-- Host server
if not openModem() then
return
end
rednet.host( "chat", sHostname )
print( "0 users connected." )
local tUsers = {}
local nUsers = 0
function send( sText, nUserID )
if nUserID then
local tUser = tUsers[ nUserID ]
if tUser then
rednet.send( tUser.nID, {
sType = "text",
nUserID = nUserID,
sText = sText,
}, "chat" )
end
else
for nUserID, tUser in pairs( tUsers ) do
rednet.send( tUser.nID, {
sType = "text",
nUserID = nUserID,
sText = sText,
}, "chat" )
end
end
end
-- Setup ping pong
local tPingPongTimer = {}
function ping( nUserID )
local tUser = tUsers[ nUserID ]
rednet.send( tUser.nID, {
sType = "ping to client",
nUserID = nUserID,
}, "chat" )
local timer = os.startTimer( 15 )
tUser.bPingPonged = false
tPingPongTimer[ timer ] = nUserID
end
function printUsers()
local x,y = term.getCursorPos()
term.setCursorPos( 1, y - 1 )
term.clearLine()
if nUsers == 1 then
print( nUsers .. " user connected." )
else
print( nUsers .. " users connected." )
end
end
-- Handle messages
local ok, error = pcall( function()
parallel.waitForAny( function()
while true do
local sEvent, timer = os.pullEvent( "timer" )
local nUserID = tPingPongTimer[ timer ]
if nUserID and tUsers[ nUserID ] then
local tUser = tUsers[ nUserID ]
if tUser then
if not tUser.bPingPonged then
send( "* "..tUser.sUsername.." has timed out" )
tUsers[ nUserID ] = nil
nUsers = nUsers - 1
printUsers()
else
ping( nUserID )
end
end
end
end
end,
function()
while true do
local tCommands
tCommands = {
["me"] = function( tUser, sContent )
if string.len(sContent) > 0 then
send( "* "..tUser.sUsername.." "..sContent )
else
send( "* Usage: /me [words]", tUser.nUserID )
end
end,
["nick"] = function( tUser, sContent )
if string.len(sContent) > 0 then
local sOldName = tUser.sUsername
tUser.sUsername = sContent
send( "* "..sOldName.." is now known as "..tUser.sUsername )
else
send( "* Usage: /nick [nickname]", tUser.nUserID )
end
end,
["users"] = function( tUser, sContent )
send( "* Connected Users:", tUser.nUserID )
local sUsers = "*"
for nUserID, tUser in pairs( tUsers ) do
sUsers = sUsers .. " " .. tUser.sUsername
end
send( sUsers, tUser.nUserID )
end,
["help"] = function( tUser, sContent )
send( "* Available commands:", tUser.nUserID )
local sCommands = "*"
for sCommand, fnCommand in pairs( tCommands ) do
sCommands = sCommands .. " /" .. sCommand
end
send( sCommands.." /logout", tUser.nUserID )
end,
}
local nSenderID, tMessage = rednet.receive( "chat" )
if type( tMessage ) == "table" then
if tMessage.sType == "login" then
-- Login from new client
local nUserID = tMessage.nUserID
local sUsername = tMessage.sUsername
if nUserID and sUsername then
tUsers[ nUserID ] = {
nID = nSenderID,
nUserID = nUserID,
sUsername = sUsername,
}
nUsers = nUsers + 1
printUsers()
send( "* "..sUsername.." has joined the chat" )
ping( nUserID )
end
else
-- Something else from existing client
local nUserID = tMessage.nUserID
local tUser = tUsers[ nUserID ]
if tUser and tUser.nID == nSenderID then
if tMessage.sType == "logout" then
send( "* "..tUser.sUsername.." has left the chat" )
tUsers[ nUserID ] = nil
nUsers = nUsers - 1
printUsers()
elseif tMessage.sType == "chat" then
local sMessage = tMessage.sText
if sMessage then
local sCommand = string.match( sMessage, "^/([a-z]+)" )
if sCommand then
local fnCommand = tCommands[ sCommand ]
if fnCommand then
local sContent = string.sub( sMessage, string.len(sCommand)+3 )
fnCommand( tUser, sContent )
else
send( "* Unrecognised command: /"..sCommand, tUser.nUserID )
end
else
send( "<"..tUser.sUsername.."> "..tMessage.sText )
end
end
elseif tMessage.sType == "ping to server" then
rednet.send( tUser.nID, {
sType = "pong to client",
nUserID = nUserID,
}, "chat" )
elseif tMessage.sType == "pong to server" then
tUser.bPingPonged = true
end
end
end
end
end
end )
end )
if not ok then
printError( error )
end
-- Unhost server
for nUserID, tUser in pairs( tUsers ) do
rednet.send( tUser.nID, {
sType = "kick",
nUserID = nUserID,
}, "chat" )
end
rednet.unhost( "chat" )
closeModem()
elseif sCommand == "join" then
-- "chat join"
-- Get hostname and username
local sHostname = tArgs[2]
local sUsername = tArgs[3]
if sHostname == nil or sUsername == nil then
printUsage()
return
end
-- Connect
if not openModem() then
return
end
write( "Looking up " .. sHostname .. "... " )
local nHostID = rednet.lookup( "chat", sHostname )
if nHostID == nil then
print( "Failed." )
return
else
print( "Success." )
end
-- Login
local nUserID = math.random( 1, 2147483647 )
rednet.send( nHostID, {
sType = "login",
nUserID = nUserID,
sUsername = sUsername,
}, "chat" )
-- Setup ping pong
local bPingPonged = true
local pingPongTimer = os.startTimer( 0 )
function ping()
rednet.send( nHostID, {
sType = "ping to server",
nUserID = nUserID,
}, "chat" )
bPingPonged = false
pingPongTimer = os.startTimer( 15 )
end
-- Handle messages
local w,h = term.getSize()
local parentTerm = term.current()
local titleWindow = window.create( parentTerm, 1, 1, w, 1, true )
local historyWindow = window.create( parentTerm, 1, 2, w, h-2, true )
local promptWindow = window.create( parentTerm, 1, h, w, 1, true )
historyWindow.setCursorPos( 1, h-2 )
term.clear()
term.setTextColour( textColour )
term.redirect( promptWindow )
promptWindow.restoreCursor()
function drawTitle()
local x,y = titleWindow.getCursorPos()
local w,h = titleWindow.getSize()
local sTitle = sUsername.." on "..sHostname
titleWindow.setTextColour( highlightColour )
titleWindow.setCursorPos( math.floor( w/2 - string.len(sTitle)/2 ), 1 )
titleWindow.clearLine()
titleWindow.write( sTitle )
promptWindow.restoreCursor()
end
function printMessage( sMessage )
term.redirect( historyWindow )
print()
if string.match( sMessage, "^\*" ) then
-- Information
term.setTextColour( highlightColour )
write( sMessage )
term.setTextColour( textColour )
else
-- Chat
local sUsernameBit = string.match( sMessage, "^\<[^\>]*\>" )
if sUsernameBit then
term.setTextColour( highlightColour )
write( sUsernameBit )
term.setTextColour( textColour )
write( string.sub( sMessage, string.len( sUsernameBit ) + 1 ) )
else
write( sMessage )
end
end
term.redirect( promptWindow )
promptWindow.restoreCursor()
end
drawTitle()
local ok, error = pcall( function()
parallel.waitForAny( function()
while true do
local sEvent, timer = os.pullEvent()
if sEvent == "timer" then
if timer == pingPongTimer then
if not bPingPonged then
printMessage( "Server timeout." )
return
else
ping()
end
end
elseif sEvent == "term_resize" then
local w,h = parentTerm.getSize()
titleWindow.reposition( 1, 1, w, 1 )
historyWindow.reposition( 1, 2, w, h-2 )
promptWindow.reposition( 1, h, w, 1 )
end
end
end,
function()
while true do
local nSenderID, tMessage = rednet.receive( "chat" )
if nSenderID == nHostID and type( tMessage ) == "table" and tMessage.nUserID == nUserID then
if tMessage.sType == "text" then
local sText = tMessage.sText
if sText then
printMessage( sText )
end
elseif tMessage.sType == "ping to client" then
rednet.send( nSenderID, {
sType = "pong to server",
nUserID = nUserID,
}, "chat" )
elseif tMessage.sType == "pong to client" then
bPingPonged = true
elseif tMessage.sType == "kick" then
return
end
end
end
end,
function()
local tSendHistory = {}
while true do
promptWindow.setCursorPos( 1,1 )
promptWindow.clearLine()
promptWindow.setTextColor( highlightColour )
promptWindow.write( ": ")
promptWindow.setTextColor( textColour )
local sChat = read( nil, tSendHistory )
if string.match( sChat, "^/logout" ) then
break
else
rednet.send( nHostID, {
sType = "chat",
nUserID = nUserID,
sText = sChat,
}, "chat" )
table.insert( tSendHistory, sChat )
end
end
end )
end )
-- Close the windows
term.redirect( parentTerm )
-- Print error notice
local w,h = term.getSize()
term.setCursorPos( 1, h )
term.clearLine()
term.setCursorBlink( false )
if not ok then
printError( error )
end
-- Logout
rednet.send( nHostID, {
sType = "logout",
nUserID = nUserID,
}, "chat" )
closeModem()
-- Print disconnection notice
print( "Disconnected." )
else
-- "chat somethingelse"
printUsage()
end