Skip to content

Instantly share code, notes, and snippets.

@CoderPuppy
Last active November 8, 2015 10:37
Show Gist options
  • Select an option

  • Save CoderPuppy/10176291 to your computer and use it in GitHub Desktop.

Select an option

Save CoderPuppy/10176291 to your computer and use it in GitHub Desktop.
Chat Server
-- Chat Server by CoderPuppy
local protocol = 'cpup/chat'
local tArgs = { ... }
local function printUsage()
print('Usages:')
print('chatserver.lua <hostname>')
end
local function openModem()
local modemFound = false
for i, side in ipairs(peripheral.getNames()) do
if peripheral.getType(side) == 'modem' then
if not rednet.isOpen(side) then
rednet.open(side)
end
modemFound = true
end
end
if not modemFound then
print('No modems found.')
end
return modemFound
end
-- 'chat host'
-- Get hostname
local hostname = tArgs[1]
if hostname == nil then
printUsage()
return
end
-- Host server
if not openModem() then
return
end
rednet.host(protocol, hostname)
print('0 users connected.')
local users = {}
local nUsers = 0
function send(msgType, msg, userID)
if userID then
local user = users[userID]
if user then
-- print('sending ' .. msgType .. ' to ' .. user.username)
-- print('sending: ' .. textutils.serialize({
-- msgType = msgType;
-- msg = msg;
-- -- userID = userID;
-- -- id = user.id;
-- username = user.username;
-- }))
rednet.send(user.id, {
side = 'server';
msgType = msgType;
msg = msg;
userID = userID;
}, protocol)
end
else
for userID in pairs(users) do
send(msgType, msg, userID)
end
end
end
local tCommands
tCommands = {
['me'] = function(user, sArgs)
if string.len(sArgs) > 0 then
send('action', { user.userID, user.username, sArgs })
else
send('msg', { -1, '@', 'Usage: /me [words]' }, user.userID)
end
end,
['nick'] = function(user, sArgs)
if string.len(sArgs) > 0 then
local oldName = user.username
user.username = sArgs
send('nick', { user.userID, oldName, sArgs })
else
send('msg', { -1, -1, '@', 'Usage: /nick [nickname]' }, user.userID)
end
end,
['users'] = function(user, sArgs)
send('msg', { -1, '@', 'Connected Users:' }, user.userID)
local sUsers = ''
for userID, user in pairs(users) do
sUsers = sUsers .. ' ' .. user.username
end
send('msg', { -1, '@', sUsers }, user.userID)
end,
['help'] = function(user, sArgs)
send('msg', { -1, '@', 'Available commands:' }, user.userID)
local sCommands = ''
for sCommand, fnCommand in pairs(tCommands) do
sCommands = sCommands .. ' /' .. sCommand
end
send('msg', { -1, '@', sCommands..' /logout' }, user.userID)
end,
}
-- Setup ping pong
local pingPongTimers = {}
function ping(userID)
local user = users[userID]
send('pingtc', nil, userID)
local timer = os.startTimer(15)
user.bPingPonged = false
pingPongTimers[timer] = userID
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 userID = pingPongTimers[timer]
if userID and users[userID] then
local user = users[userID]
if user then
if not user.bPingPonged then
send('leave', { user.userID, user.username, 'Timeout' })
users[userID] = nil
nUsers = nUsers - 1
printUsers()
else
ping(userID)
end
end
end
end
end,
function()
while true do
local nSenderID, msg = rednet.receive(protocol)
if type(msg) == 'table' then
local userID = msg.userID
local user = users[userID]
-- print('got: ' .. textutils.serialize({
-- msgType = msg.msgType;
-- msg = msg.msg;
-- -- userID = userID;
-- -- nSenderID = nSenderID;
-- username = user and user.username or msg.msg
-- }))
if msg.msgType == 'login' then
-- Login from new client
local username = msg.msg
if userID and username then
users[userID] = {
id = nSenderID;
userID = userID;
username = username;
}
nUsers = nUsers + 1
printUsers()
send('join', { userID, username })
ping(userID)
end
else
-- Something else from existing client
if user and user.id == nSenderID then
-- print(msg.msgType)
if msg.msgType == 'logout' then
send('leave', { user.userID, user.username, msg.msg })
users[userID] = nil
nUsers = nUsers - 1
printUsers()
elseif msg.msgType == 'chat' then
local chat = msg.msg
if chat then
local sCommand = string.match(chat, '^/([a-z]+)')
if sCommand then
local fnCommand = tCommands[sCommand]
if fnCommand then
local sArgs = string.sub(chat, string.len(sCommand)+3)
fnCommand(user, sArgs)
else
send('msg', { -1, '@', 'Unrecognised command: /'..sCommand }, user.userID)
end
else
send('msg', { user.userID, user.username, chat })
end
end
elseif msg.msgType == 'pingts' then
send('pongtc', nil, user.userID)
elseif msg.msgType == 'pongts' then
user.bPingPonged = true
end
end
end
end
end
end)
end)
if not ok then
printError(error)
end
-- Unhost server
send('kick')
rednet.unhost(protocol)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment