ldd-CC/disknet.lua

332 lines
8.9 KiB
Lua
Raw Normal View History

2020-11-28 06:21:44 +00:00
--[[
DISKNET - by LDDestroier
Refer to https://github.com/LDDestroier/CC/blob/master/disknet.md for documentation
--]]
local disknet = disknet or {}
2019-05-02 00:51:40 +00:00
2019-05-02 04:04:12 +00:00
local tArg = {...}
disknet.mainPath = "disk/DISKNET" -- path of shared file
local limitChannelsToModem = false -- if true, can only use number channels from 1 to 65535
local maximumBufferSize = 64 -- largest amount of messages per channel buffered
2020-11-28 06:20:49 +00:00
disknet.checkDelay = 0.2 -- amount of time (seconds) between checking the file -- if 0, checks super fast so don't do that
local isUsingTweaked = false
if _HOST then
2020-11-28 06:07:52 +00:00
if _HOST:find("CCEmuX") or _HOST:find("CC:Tweaked") or _HOST:find("[(]Minecraft") then
isUsingTweaked = true
end
end
2019-05-02 04:04:12 +00:00
2019-05-02 00:51:40 +00:00
local openChannels = {}
local yourID = os.getComputerID()
2019-05-02 04:04:12 +00:00
local uniqueID = math.random(1, 2^31 - 1) -- prevents receiving your own messages
disknet.msgCheckList = {} -- makes sure duplicate messages aren't received
local ageToToss = 0.005 -- amount of time before a message is removed
2019-05-02 00:51:40 +00:00
-- used for synching times between different emulators
disknet._timeMod = 0
2020-04-02 23:10:27 +00:00
-- do not think for one second that os.epoch("utc") would be a proper substitute
2019-05-02 00:51:40 +00:00
local getTime = function()
if os.day then
return (os.time() + (-1 + os.day()) * 24) + disknet._timeMod
else
return os.time() + disknet._timeMod
end
2019-05-02 00:51:40 +00:00
end
2020-04-02 23:10:27 +00:00
local function serialize(tbl)
local output = "{"
local noKlist = {}
local cc = table.concat
for i = 1, #tbl do
if type(tbl[i]) == "table" then
output = output .. serialize(tbl[i])
elseif type(tbl[i]) == "string" then
output = cc({output, "\"", tbl[i], "\""})
else
output = output .. tbl[i]
end
noKlist[i] = true
output = output .. ","
end
for k,v in pairs(tbl) do
if not noKlist[k] then
if type(k) == "number" or type(k) == "table" then
output = cc({output, "[", k, "]="})
else
output = cc({output, k, "="})
end
if type(v) == "table" then
output = output .. serialize(v)
elseif type(v) == "string" then
output = cc({output, "\"", v, "\""})
else
output = output .. v
end
output = output .. ","
end
end
return output:sub(1, -2):gsub("\n", "\\n") .. "}"
end
2019-05-02 04:04:12 +00:00
local readFile = function(path)
local file = fs.open(path, "r")
local contents = file.readAll()
file.close()
return contents
end
local writeFile = function(path, contents)
local file = fs.open(path, "w")
file.write(contents)
file.close()
end
-- if 'limitChannelsToModem', then will make sure that channel is a number between 0 and 65535
2019-05-02 00:51:40 +00:00
local checkValidChannel = function(channel)
if limitChannelsToModem then
if type(channel) == "number" then
if channel < 0 or channel > 65535 then
return false, "channel must be between 0 and 65535"
else
return true
end
else
return false, "channel must be number"
end
else
if type(channel) == "string" or type(channel) == "number" then
return true
else
return false, "channel must be castable to string"
end
end
end
disknet.isOpen = function(channel)
local valid, grr = checkValidChannel(channel)
if valid then
for i = 1, #openChannels do
if openChannels[i] == channel then
return true
end
end
return false
else
error(grr)
end
end
isOpen = disknet.isOpen
2019-05-02 00:51:40 +00:00
disknet.open = function(channel)
local valid, grr = checkValidChannel(channel)
if valid then
openChannels[#openChannels + 1] = channel
return true
else
error(grr)
end
end
open = disknet.open
2019-05-02 00:51:40 +00:00
disknet.close = function(channel)
local valid, grr = checkValidChannel(channel)
if valid then
for i = 1, #openChannels do
if openChannels[i] == channel then
table.remove(openChannels, i)
return true
end
end
return false
else
error(grr)
end
end
close = disknet.close
2019-05-02 00:51:40 +00:00
disknet.closeAll = function()
openChannels = {}
end
closeAll = disknet.closeAll
2019-05-02 00:51:40 +00:00
disknet.send = function(channel, message, recipient)
2019-05-02 00:51:40 +00:00
local valid, grr = checkValidChannel(channel)
if valid then
2019-05-02 04:17:37 +00:00
if not fs.exists(fs.combine(disknet.mainPath, tostring(channel))) then
fs.makeDir(disknet.mainPath)
2019-05-02 04:17:37 +00:00
fs.open(fs.combine(disknet.mainPath, tostring(channel)), "w").close()
end
2019-05-02 04:04:12 +00:00
local contents = textutils.unserialize(readFile(fs.combine(disknet.mainPath, tostring(channel))))
local cTime = getTime()
2019-05-02 00:51:40 +00:00
if disknet.isOpen(channel) then
2019-05-02 04:04:12 +00:00
local file = fs.open(fs.combine(disknet.mainPath, tostring(channel)), "w")
if contents then
contents[#contents + 1] = {
time = cTime,
2019-05-02 04:04:12 +00:00
id = yourID,
uniqueID = uniqueID,
messageID = math.random(1, 2^31 - 1),
channel = channel,
recipient = recipient,
2019-05-02 04:04:12 +00:00
message = message,
}
for i = #contents, 1, -1 do
if cTime - (contents[i].time or 0) > ageToToss then
table.remove(contents, i)
end
end
2019-05-02 04:04:12 +00:00
if #contents > maximumBufferSize then
table.remove(contents, 1)
end
2020-04-02 23:10:27 +00:00
file.write(serialize(contents))
2019-05-02 04:04:12 +00:00
else
2020-04-02 23:10:27 +00:00
file.write(serialize({{
time = cTime,
2019-05-02 04:04:12 +00:00
id = yourID,
uniqueID = uniqueID,
messageID = math.random(1, 2^31 - 1),
channel = channel,
message = message,
}}):gsub("\n[ ]*", ""))
2019-05-02 04:04:12 +00:00
end
2019-05-02 00:51:40 +00:00
file.close()
return true
else
return false
end
else
error(grr)
end
end
send = disknet.send
2019-05-02 00:51:40 +00:00
local fList, pList, sList = {}, {}, {}
2019-05-02 04:04:12 +00:00
local loadFList = function()
fList, pList = {}, {}
if channel then
fList = {fs.open(fs.combine(disknet.mainPath, tostring(channel)), "r")}
pList = {fs.combine(disknet.mainPath, tostring(channel))}
else
for i = 1, #openChannels do
fList[i] = fs.open(fs.combine(disknet.mainPath, tostring(openChannels[i])), "r")
pList[i] = fs.combine(disknet.mainPath, tostring(openChannels[i]))
end
end
end
-- returns: string message, string/number channel, number senderID, number timeThatMessageWasSentAt
disknet.receive = function(channel, senderFilter)
2019-05-02 00:51:40 +00:00
local valid, grr = checkValidChannel(channel)
if valid or not channel then
2019-05-02 15:00:43 +00:00
2019-05-02 04:04:12 +00:00
local output, contents
local doRewrite = false
2019-05-02 15:00:43 +00:00
local good, goddamnit = pcall(function()
local cTime = getTime()
local goWithIt = false
while true do
loadFList()
for i = 1, #fList do
contents = fList[i].readAll()
if contents ~= "" then
contents = textutils.unserialize(contents)
if type(contents) == "table" then
if contents[1] then
if not output then
for look = 1, #contents do
if (contents[look].uniqueID ~= uniqueID) and (not disknet.msgCheckList[contents[look].messageID]) then -- make sure you're not receiving messages that you sent
if (not contents[look].recipient) or contents[look].recipient == yourID then -- make sure that messages intended for others aren't picked up
if (not channel) or channel == contents[look].channel then -- make sure that messages are the same channel as the filter, if any
if (not senderFilter) or senderFilter == contents[look].id then -- make sure that the sender is the same as the id filter, if any
if (not isUsingTweaked) and math.abs(contents[look].time - getTime()) >= ageToToss then -- if using something besides CC:Tweaked/CCEmuX, adjust your time.
disknet._timeMod = contents[look].time - getTime()
cTime = getTime()
goWithIt = true
end
if cTime - (contents[look].time or 0) <= ageToToss or goWithIt then -- make sure the message isn't too old
disknet.msgCheckList[contents[look].messageID] = true
output = {}
for k,v in pairs(contents[look]) do
output[k] = v
end
break
end
end
end
2019-05-02 04:53:13 +00:00
end
end
2019-05-02 04:04:12 +00:00
end
end
2019-05-02 15:00:43 +00:00
-- delete old msesages
doRewrite = false
for t = #contents, 1, -1 do
if cTime - (contents[t].time or 0) > ageToToss or cTime - (contents[t].time or 0) < -1 then
disknet.msgCheckList[contents[t].messageID] = nil
table.remove(contents, t)
doRewrite = true
end
2019-05-02 04:04:12 +00:00
end
if doRewrite then
2020-04-02 23:10:27 +00:00
writeFile(pList[i], serialize(contents))
end
if output then
break
2019-05-02 04:04:12 +00:00
end
end
end
end
2019-05-02 00:51:40 +00:00
end
if output then
break
else
2020-11-28 06:20:49 +00:00
if disknet.checkDelay > 0 then
sleep(disknet.checkDelay)
2019-05-09 05:56:23 +00:00
else
os.queueEvent("")
os.pullEvent("")
end
end
for i = 1, #fList do
fList[i].close()
end
fList, pList = {}, {}
2019-05-02 00:51:40 +00:00
end
end)
if good then
if contents then
return output.message, output.channel, output.id, output.time + disknet._timeMod
2019-05-02 00:51:40 +00:00
else
return nil
2019-05-02 00:51:40 +00:00
end
else
for i = 1, #fList do
fList[i].close()
end
error(goddamnit, 0)
2019-05-02 00:51:40 +00:00
end
else
error(grr)
end
end
receive = disknet.receive
2019-05-02 00:51:40 +00:00
-- not really needed if going between CCEmuX and another emulator, but may be needed between two separate CCEmuX daemons
disknet.receive_TS = function(...)
local message, channel, id, time = disknet.receive(...)
if time then
disknet._timeMod = time - getTime()
end
return message, channel, id, time
end
receive_TS = disknet.receive_TS
2019-05-02 00:51:40 +00:00
return disknet