MineOS/lib/modemConnection.lua
Igor 3df7e1b0f0 Обновка чата, выходим в альфу
Добавлена поддержка аватарок, пофикшена (вроде бы!) работа с либой
беспроводных соединений, добавлена фича, считающая кол-во непрочитанных
сообщений, добавлен выбор собеседника
2016-03-15 03:43:58 +03:00

497 lines
18 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local event = require("event")
local computer = require("computer")
local component = require("component")
local term = require("term")
local serialization = require("serialization")
local unicode = require("unicode")
local ecs, image
local modem = component.modem
local gpu = component.gpu
local modemConnection = {}
----------------------------------------------------------------------------------------------------------------------------------
modemConnection.port = 322
modemConnection.waitForConnectionAcceptingDelay = 10
modemConnection.receiveMessagesFromRobots = true
modemConnection.receiveMessagesFromTablets = true
modemConnection.receiveMessagesFromComputers = true
local infoMessages = {
userTriesToConnectNoGUI = "Пользователь %s желает установить с вами соединение. Разрешить? Y/N",
noModem = "Этой библиотеке требуется сетевая карта для работы",
}
----------------------------------------------------------------------------------------------------------------------------------
local xSize, ySize = gpu.getResolution()
local computerIcon, robotIcon, tabletIcon
if not component.isAvailable("robot") then
image = require("image")
ecs = require("ECSAPI")
computerIcon = image.load("MineOS/System/OS/Icons/Computer.pic")
robotIcon = image.load("MineOS/System/OS/Icons/Robot.pic")
tabletIcon = image.load("MineOS/System/OS/Icons/Tablet.pic")
end
----------------------------------------------------------------------------------------------------------------------------------
local obj = {}
local function newObj(class, name, ...)
obj[class] = obj[class] or {}
obj[class][name] = {...}
end
local function sendAcceptingMessage(address)
modem.send(address, modemConnection.port, "connectionAccepted", modemConnection.dataToSend)
end
local function sendDecliningMessage(address)
modem.send(address, modemConnection.port, "connectionDeclined", modemConnection.dataToSend)
end
local function tryToConnect(address)
modem.send(address, modemConnection.port, "iWantToConnect", modemConnection.dataToSend)
end
local function acceptingOrDecliningDialog(address, accepted)
local text1, text2
-- if accepted == true then
-- text1 = "Установлено соединение с пользователем"
-- text2 = "\"" .. ecs.stringLimit("end", address, 18) .. "\""
-- else
if accepted == false then
text1 = "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" отказался"
text2 = "установить с вами соединение."
elseif accepted == nil then
text1 = "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" не ответил"
text2 = "на ваш запрос"
end
ecs.universalWindow("auto", "auto", 36, 0x262626, true,
{"EmptyLine"},
{"CenterText", ecs.colors.orange, "WirelessConnection"},
{"EmptyLine"},
{"CenterText", 0xffffff, text1 },
{"CenterText", 0xffffff, text2 },
{"EmptyLine"},
{"Button", {ecs.colors.orange, 0x262626, "OK"}}
)
end
local function askForAcceptConnection(address)
if not component.isAvailable("robot") then
local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true,
{"EmptyLine"},
{"CenterText", ecs.colors.orange, "WirelessConnection"},
{"EmptyLine"},
{"CenterText", 0xffffff, "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" желает" },
{"CenterText", 0xffffff, "установить с вами беспроводное соединение." },
{"EmptyLine"},
{"CenterText", 0xffffff, "Разрешить подключение?" },
{"EmptyLine"},
{"Button", {ecs.colors.orange, 0x262626, "Да"}, {0x999999, 0xffffff, "Нет"}}
)
if data[1] == "Да" then
modemConnection.remoteAddress = address
sendAcceptingMessage(address)
computer.pushSignal("connectionEstabilishedExitFromGUI")
else
sendDecliningMessage(address)
end
else
term.clear()
term.setCursor(1, 1)
print("Пользователь \"" .. string.sub(address, 1, 12) .. "\" желает установить с вами беспроводное соединение. Разрешить подключение? Y/N")
local answer = term.read()
if string.sub(string.lower(answer), 1, 1) == "y" then
modemConnection.remoteAddress = address
sendAcceptingMessage(address)
print(" ")
print("Соединение установлено.")
print(" ")
-- computer.pushSignal("key_down", component.getPrimary("keyboard").address, 13, 28, "ECS")
else
sendDecliningMessage(address)
term.clear()
term.setCursor(1, 1)
end
end
end
local function infoAboutClient(userData)
local arguments = {
"auto", "auto", 36, 0x262626, true, {"EmptyLine"}, {"CenterText", ecs.colors.orange, "Информация о пользователе"}, {"EmptyLine"},
}
if userData.isRobot then
table.insert(arguments, {"CenterText", 0xffffff, "Тип: робот"})
elseif userData.isTablet then
table.insert(arguments, {"CenterText", 0xffffff, "Тип: планшет"})
elseif not userData.isTablet and not userData.isRobot then
table.insert(arguments, {"CenterText", 0xffffff, "Тип: компьютер"})
end
table.insert(arguments, {"CenterText", 0xffffff, "Имя: " .. userData.name})
table.insert(arguments, {"CenterText", 0xffffff, "Адрес: " .. ecs.stringLimit("end", userData.address, 12)})
table.insert(arguments, {"CenterText", 0xffffff, "Память: " .. userData.ram .. " KB"})
if userData.isTablet or userData.isRobot and userData.hasUpgrades then
table.insert(arguments, {"EmptyLine"})
table.insert(arguments, {"CenterText", ecs.colors.orange, "Улучшения"})
table.insert(arguments, {"EmptyLine"})
if userData.inventoryController then
table.insert(arguments, {"CenterText", 0xffffff, "Контроллер инвентаря"})
end
if userData.tankController then
table.insert(arguments, {"CenterText", 0xffffff, "Контроллер бака"})
end
if userData.crafting then
table.insert(arguments, {"CenterText", 0xffffff, "Крафтинг"})
end
if userData.redstone then
table.insert(arguments, {"CenterText", 0xffffff, "Редстоун-плата"})
end
if userData.navigation then
table.insert(arguments, {"CenterText", 0xffffff, "Навигация"})
end
if userData.piston then
table.insert(arguments, {"CenterText", 0xffffff, "Поршень"})
end
if userData.geolyzer then
table.insert(arguments, {"CenterText", 0xffffff, "Геоанализатор"})
end
end
table.insert(arguments, {"EmptyLine"})
table.insert(arguments, {"Button", {ecs.colors.orange, 0x262626, "OK"}})
ecs.universalWindow(table.unpack(arguments))
end
local function modemMessageHandler(_, localAddress, remoteAddress, port, distance, ...)
local messages = {...}
if #messages > 0 then
if port == modemConnection.port then
if messages[1] == "iWantToConnect" and not modemConnection.remoteAddress then
askForAcceptConnection(remoteAddress)
elseif messages[1] == "iAmHereAddMePlease" and not modemConnection.remoteAddress then
if not modemConnection.availableUsers[remoteAddress] then
local userData = serialization.unserialize(messages[2])
if
(not userData.isRobot and not userData.isTablet and modemConnection.receiveMessagesFromComputers)
or
(userData.isRobot and modemConnection.receiveMessagesFromRobots)
or
(userData.isTablet and modemConnection.receiveMessagesFromTablets)
then
modemConnection.availableUsers[userData.address] = userData
modem.send(remoteAddress, modemConnection.port, "iAmHereAddMePlease", modemConnection.dataToSend)
computer.pushSignal("userlistChanged")
end
end
elseif messages[1] == "iAmDisconnecting" then
if modemConnection.availableUsers[remoteAddress] then
modemConnection.availableUsers[remoteAddress] = nil
computer.pushSignal("userlistChanged")
end
end
end
end
end
local function createSendingArray()
modemConnection.dataToSend = {}
modemConnection.dataToSend.address = modemConnection.localAddress
modemConnection.dataToSend.name = component.filesystem.getLabel()
modemConnection.dataToSend.ram = math.floor(computer.totalMemory() / 1024)
if component.isAvailable("robot") then
modemConnection.dataToSend.isRobot = true
if component.isAvailable("inventory_controller") then
modemConnection.dataToSend.inventoryController = true; modemConnection.dataToSend.hasUpgrades = true
end
if component.isAvailable("tank_controller") then
modemConnection.dataToSend.tankController = true; modemConnection.dataToSend.hasUpgrades = true
end
if component.isAvailable("crafting") then
modemConnection.dataToSend.crafting = true; modemConnection.dataToSend.hasUpgrades = true
end
if component.isAvailable("redstone") then
modemConnection.dataToSend.redstone = true; modemConnection.dataToSend.hasUpgrades = true
end
end
if component.isAvailable("tablet") then
modemConnection.dataToSend.isTablet = true
if component.isAvailable("navigation") then
modemConnection.dataToSend.navigation = true; modemConnection.dataToSend.hasUpgrades = true
end
if component.isAvailable("piston") then
modemConnection.dataToSend.piston = true; modemConnection.dataToSend.hasUpgrades = true
end
end
if component.isAvailable("geolyzer") then
modemConnection.dataToSend.geolyzer = true; modemConnection.dataToSend.hasUpgrades = true
end
modemConnection.dataToSend = serialization.serialize(modemConnection.dataToSend)
end
--Нарисовать окружность, алгоритм спизжен с вики
local function circle(xCenter, yCenter, radius, color)
gpu.setBackground(color)
local function insertPoints(x, y)
gpu.set(xCenter + x * 2, yCenter + y, " ")
gpu.set(xCenter + x * 2, yCenter - y, " ")
gpu.set(xCenter - x * 2, yCenter + y, " ")
gpu.set(xCenter - x * 2, yCenter - y, " ")
gpu.set(xCenter + x * 2 + 1, yCenter + y, " ")
gpu.set(xCenter + x * 2 + 1, yCenter - y, " ")
gpu.set(xCenter - x * 2 + 1, yCenter + y, " ")
gpu.set(xCenter - x * 2 + 1, yCenter - y, " ")
end
local x = 0
local y = radius
local delta = 3 - 2 * radius;
while (x < y) do
insertPoints(x, y);
insertPoints(y, x);
if (delta < 0) then
delta = delta + (4 * x + 6)
else
delta = delta + (4 * (x - y) + 10)
y = y - 1
end
x = x + 1
end
if x == y then insertPoints(x, y) end
end
local function drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius)
for radius = minumumRadius, maximumRadius, step do
if radius == currentRadius then
circle(xCircle, yCircle, radius, 0x888888)
else
circle(xCircle, yCircle, radius, 0xDDDDDD)
end
end
end
local function drawIconAndAddress(x, y, background, foreground, userData)
if userData.isRobot then
image.draw(x + 3, y, robotIcon)
elseif userData.isTablet then
image.draw(x + 3, y, tabletIcon)
else
image.draw(x + 3, y, computerIcon)
end
ecs.colorTextWithBack(x, y + 5, foreground, background, ecs.stringLimit("end", userData.address, 14))
return x, y, x + 13, y + 5
end
local function drawHorizontalIcons()
local height = 8
local y = math.floor(ySize / 2 - height / 2)
local background = 0x66A8FF
ecs.square(1, y, xSize, height, background)
local iconWidth = 14
local spaceBetween = 2
local totalWidth = ecs.getArraySize(modemConnection.availableUsers) * (iconWidth + spaceBetween) - spaceBetween
local x = math.floor(xSize / 2 - totalWidth / 2) + 1
obj.Users = {}
local counter = 0
local limit = math.floor(xSize / (iconWidth + spaceBetween))
y = y + 1
for address in pairs(modemConnection.availableUsers) do
if counter < limit then
newObj("Users", address, drawIconAndAddress(x, y, background, 0xFFFFFF, modemConnection.availableUsers[address]))
end
x = x + iconWidth + spaceBetween
counter = counter + 1
end
end
local function drawSelectedIcon(x, y, background, foreground, userData)
local selectionWidth = 16
local skokaOtnat = (selectionWidth - 14) / 2
local oldPixels = ecs.rememberOldPixels(x - skokaOtnat, y, x + selectionWidth - 2, y + 13)
ecs.square(x - skokaOtnat, y, selectionWidth, 8, background)
drawIconAndAddress(x, y + 1, background, foreground, userData)
obj.CykaKnopkaInfo = { ecs.drawButton(x - skokaOtnat, y + 8, selectionWidth, 3, "Информация", 0xff6699, 0xFFFFFF) }
obj.CykaKnopkaConnect = { ecs.drawButton(x - skokaOtnat, y + 11, selectionWidth, 3, "Подключиться", 0xff3333, 0xFFFFFF) }
obj.CykaKnopkaConnect.address = userData.address
return oldPixels
end
local function connectionGUI()
ecs.square(1, 1, xSize, ySize, 0xEEEEEE)
local xCircle, yCircle = math.floor(xSize / 2), ySize - 3
local minumumRadius, maximumRadius = 7, xCircle * 0.8
local step = 4
local currentRadius = minumumRadius
local unserializedDataToSend = serialization.unserialize(modemConnection.dataToSend)
drawIconAndAddress(xCircle - 6, ySize - 6, 0xEEEEEE, 0x262626, unserializedDataToSend)
while true do
if ecs.getArraySize(modemConnection.availableUsers) > 0 then
currentRadius = 0
drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius)
drawHorizontalIcons()
local oldPixels, needToUpdate
while true do
if not oldPixels and needToUpdate then
if ecs.getArraySize(modemConnection.availableUsers) <= 0 then
ecs.square(1, 1, xSize, ySize, 0xEEEEEE)
drawIconAndAddress(xCircle - 6, ySize - 6, 0xEEEEEE, 0x262626, unserializedDataToSend)
currentRadius = minumumRadius
break
else
drawHorizontalIcons()
needToUpdate = false
end
end
local e = { event.pull() }
if e[1] == "touch" then
if obj.CykaKnopkaInfo and obj.CykaKnopkaConnect then
if ecs.clickedAtArea(e[3], e[4], obj.CykaKnopkaInfo[1], obj.CykaKnopkaInfo[2], obj.CykaKnopkaInfo[3], obj.CykaKnopkaInfo[4]) then
ecs.drawButton(obj.CykaKnopkaInfo[1], obj.CykaKnopkaInfo[2], 16, 3, "Информация", 0x262626, 0xFFFFFF)
os.sleep(0.2)
if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end
infoAboutClient(modemConnection.availableUsers[obj.CykaKnopkaConnect.address])
elseif ecs.clickedAtArea(e[3], e[4], obj.CykaKnopkaConnect[1], obj.CykaKnopkaConnect[2], obj.CykaKnopkaConnect[3], obj.CykaKnopkaConnect[4]) then
ecs.drawButton(obj.CykaKnopkaConnect[1], obj.CykaKnopkaConnect[2], 16, 3, "Подключиться", 0x262626, 0xFFFFFF)
os.sleep(0.2)
if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end
local oldInfoPixels = ecs.info("auto", "auto", "", "Ожидание ответа от пользователя...")
tryToConnect(obj.CykaKnopkaConnect.address)
local function filter(name, _, remoteAddress)
if name == "modem_message" and remoteAddress == obj.CykaKnopkaConnect.address then
return true
end
end
local e2 = { event.pullFiltered(modemConnection.waitForConnectionAcceptingDelay, filter) }
ecs.drawOldPixels(oldInfoPixels)
if e2[6] == "connectionAccepted" then
modemConnection.remoteAddress = e2[3]
-- acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, true)
computer.pushSignal("connectionEstabilishedExitFromGUI")
elseif e2[6] == "connectionDeclined" then
acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, false)
else
acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, nil)
end
end
obj.CykaKnopkaInfo, obj.CykaKnopkaConnect = nil, nil
end
if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end
for address in pairs(obj.Users) do
if ecs.clickedAtArea(e[3], e[4], obj.Users[address][1], obj.Users[address][2], obj.Users[address][3], obj.Users[address][4]) then
oldPixels = drawSelectedIcon(obj.Users[address][1], obj.Users[address][2] - 1, 0xCCCCFF, 0x262626, modemConnection.availableUsers[address])
break
end
end
elseif e[1] == "userlistChanged" then
needToUpdate = true
elseif e[1] == "connectionEstabilishedExitFromGUI" then
ecs.prepareToExit()
modemConnection.disconnect()
return
end
end
else
drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius)
currentRadius = currentRadius + step
if currentRadius > (maximumRadius + step) then currentRadius = minumumRadius end
os.sleep(0)
end
end
end
----------------------------------------------------------------------------------------------------------------------------------
function modemConnection.stopReceivingData()
event.ignore("modem_message", modemMessageHandler)
end
function modemConnection.startReceivingData()
modemConnection.stopReceivingData()
modemConnection.remoteAddress = nil
event.listen("modem_message", modemMessageHandler)
end
function modemConnection.disconnect()
modem.broadcast(modemConnection.port, "iAmDisconnecting")
end
function modemConnection.sendPersonalData()
modem.broadcast(modemConnection.port, "iAmHereAddMePlease", modemConnection.dataToSend)
end
function modemConnection.changePort(newPort)
modem.close(modemConnection.port)
modem.open(newPort)
modemConnection.port = newPort
modemConnection.remoteAddress = nil
modemConnection.availableUsers = {}
modemConnection.localAddress = component.getPrimary("modem").address
createSendingArray()
end
function modemConnection.search()
modemConnection.availableUsers = {}
modemConnection.remoteAddress = nil
modemConnection.disconnect()
modemConnection.sendPersonalData()
connectionGUI()
end
function modemConnection.init()
if component.isAvailable("modem") then
modemConnection.changePort(modemConnection.port)
else
ecs.error(infoMessages.noModem)
return
end
end
----------------------------------------------------------------------------------------------------------------------------------
modemConnection.init()
----------------------------------------------------------------------------------------------------------------------------------
return modemConnection