Permalink
Cannot retrieve contributors at this time
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?
CC/disknet.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
331 lines (306 sloc)
8.88 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --[[ | |
| DISKNET - by LDDestroier | |
| Refer to https://github.com/LDDestroier/CC/blob/master/disknet.md for documentation | |
| --]] | |
| local disknet = disknet or {} | |
| 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 | |
| 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 | |
| if _HOST:find("CCEmuX") or _HOST:find("CC:Tweaked") or _HOST:find("[(]Minecraft") then | |
| isUsingTweaked = true | |
| end | |
| end | |
| local openChannels = {} | |
| local yourID = os.getComputerID() | |
| 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 | |
| -- used for synching times between different emulators | |
| disknet._timeMod = 0 | |
| -- do not think for one second that os.epoch("utc") would be a proper substitute | |
| local getTime = function() | |
| if os.day then | |
| return (os.time() + (-1 + os.day()) * 24) + disknet._timeMod | |
| else | |
| return os.time() + disknet._timeMod | |
| end | |
| end | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| disknet.closeAll = function() | |
| openChannels = {} | |
| end | |
| closeAll = disknet.closeAll | |
| disknet.send = function(channel, message, recipient) | |
| local valid, grr = checkValidChannel(channel) | |
| if valid then | |
| if not fs.exists(fs.combine(disknet.mainPath, tostring(channel))) then | |
| fs.makeDir(disknet.mainPath) | |
| fs.open(fs.combine(disknet.mainPath, tostring(channel)), "w").close() | |
| end | |
| local contents = textutils.unserialize(readFile(fs.combine(disknet.mainPath, tostring(channel)))) | |
| local cTime = getTime() | |
| if disknet.isOpen(channel) then | |
| local file = fs.open(fs.combine(disknet.mainPath, tostring(channel)), "w") | |
| if contents then | |
| contents[#contents + 1] = { | |
| time = cTime, | |
| id = yourID, | |
| uniqueID = uniqueID, | |
| messageID = math.random(1, 2^31 - 1), | |
| channel = channel, | |
| recipient = recipient, | |
| message = message, | |
| } | |
| for i = #contents, 1, -1 do | |
| if cTime - (contents[i].time or 0) > ageToToss then | |
| table.remove(contents, i) | |
| end | |
| end | |
| if #contents > maximumBufferSize then | |
| table.remove(contents, 1) | |
| end | |
| file.write(serialize(contents)) | |
| else | |
| file.write(serialize({{ | |
| time = cTime, | |
| id = yourID, | |
| uniqueID = uniqueID, | |
| messageID = math.random(1, 2^31 - 1), | |
| channel = channel, | |
| message = message, | |
| }}):gsub("\n[ ]*", "")) | |
| end | |
| file.close() | |
| return true | |
| else | |
| return false | |
| end | |
| else | |
| error(grr) | |
| end | |
| end | |
| send = disknet.send | |
| local fList, pList, sList = {}, {}, {} | |
| 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) | |
| local valid, grr = checkValidChannel(channel) | |
| if valid or not channel then | |
| local output, contents | |
| local doRewrite = false | |
| 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 | |
| end | |
| end | |
| end | |
| end | |
| -- 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 | |
| end | |
| if doRewrite then | |
| writeFile(pList[i], serialize(contents)) | |
| end | |
| if output then | |
| break | |
| end | |
| end | |
| end | |
| end | |
| end | |
| if output then | |
| break | |
| else | |
| if disknet.checkDelay > 0 then | |
| sleep(disknet.checkDelay) | |
| else | |
| os.queueEvent("") | |
| os.pullEvent("") | |
| end | |
| end | |
| for i = 1, #fList do | |
| fList[i].close() | |
| end | |
| fList, pList = {}, {} | |
| end | |
| end) | |
| if good then | |
| if contents then | |
| return output.message, output.channel, output.id, output.time + disknet._timeMod | |
| else | |
| return nil | |
| end | |
| else | |
| for i = 1, #fList do | |
| fList[i].close() | |
| end | |
| error(goddamnit, 0) | |
| end | |
| else | |
| error(grr) | |
| end | |
| end | |
| receive = disknet.receive | |
| -- 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 | |
| return disknet |