Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Gmail-Server PaymentOption
- Google on BlackWolfCC
- 23 November 2012
- Mail server script to correspond and handle
- mail requests within a 50 block radius
- (or larger depending on CC config file)
- from the Gmail-Client script.
- Basic Model:
- - Client -> Server -> Receiver -> Server(Confirmation)
- -> Client(Confirmation) or Stored to resend until
- confirmation received.
- Message Model:
- {sender = (number), receiver = (number),
- message = (table), subject = (string),
- attachment = {code = (string)}}
- - Attachments will only be sent if the attachment table
- exists within the message table provided.
- - The whole message will be in a table, and therefore
- will be serialized and unserialized upon sending and
- receiving.
- ]]--
- -- Variables --
- local unsentMessagesDirectory = "Gmail-Server/Unsent_Mail" -- The directory in which
- -- mail that hasn't been
- -- sent is stored.
- local listenTimeout = 10 -- The time in seconds for the server to wait and listen
- -- for incoming mail before attempting to resend all
- -- unsent messages.
- local confirmationTimeout = 0.3 -- The time in seconds for the server to wait
- -- for a confirmation message from a receiver.
- local confirmationFormat = "ConfirmedReception" -- The message that must be
- -- received from a receiver to
- -- confirm a successful message
- -- send.
- local sentMessageCount = 0 -- The number of messages that have been sent.
- local screenWidth, screenHeight = term.getSize() -- The x and y dimensions of the
- -- screen we're on.
- local exitOption = {label = "Exit", xPos = 1, yPos = screenHeight}
- -- Variables --
- -- Rednet Functions --
- -- Opens or close any modem found on this computer. An error is thrown if
- -- no modem on the computer was found.
- -- Params : open - A boolean value that determines whehter or not the modem is
- -- opened or closed.
- -- Returns: nil
- function openOrCloseModem(open)
- for sideIndex, side in pairs(rs.getSides()) do
- if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
- if open then
- rednet.open(side)
- else
- rednet.close(side)
- end
- return
- end
- end
- error("No modem found. Aborting.")
- end
- -- Sends a message provided in its unserialized for in a serialized format
- -- to the receiver. After sending a message the function waits for 0.3 seconds
- -- for a confirmation; if received the function returns true; if not, false.
- -- Params : unserializedMessage - The unserializedMessage table.
- -- Returns: true or false - Depending on whether or not a confirmation was
- -- received from the receiver.
- function sendUnserializedMessage(unserializedMessage)
- local receiverID = unserializedMessage.receiver
- rednet.send(receiverID, textutils.serialize(unserializedMessage))
- local confirmationID, confirmationMessage = rednet.receive(confirmationTimeout)
- if confirmationID == receiverID and confirmationMessage == confirmationFormat then
- sentMessageCount = sentMessageCount + 1
- return true
- end
- return false
- end
- -- Resends all of the unsent messages from the unsent messages directory from
- -- top to bottom, waiting for confirmations after each one. If an unsent
- -- message is resent and the server receives a confirmation of reception, then
- -- the unsent message will be delete from the unsent messages directory.
- -- Params : nil
- -- Returns: nil
- function resendUnsentMessages()
- local unsentMessagesDirectoryContents = fs.list(unsentMessagesDirectory)
- for index, unsentMessageName in ipairs(unsentMessagesDirectoryContents) do
- local unserializedMessage = loadUnsentMessageInUnserializedFormat(unsentMessageName)
- if sendUnserializedMessage(unserializedMessage) then
- deleteUnsentMessage(unsentMessageName)
- end
- end
- end
- -- Listens for incoming messages for 10 seconds, then resends all unsent messages
- -- , only to re-listen for incoming messages to send. This function runs in an infinite
- -- loop. This should be used as the main function for listening.
- -- Params : nil
- -- Returns: nil
- function listenForIncomingMail()
- local listenTimer = os.startTimer(listenTimeout)
- local updateTimer = os.startTimer(1) -- The amount of time to wait before
- -- updating the screen.
- while true do
- -- Redraw the main screen with the updated information.
- drawMainScreen()
- local event, incomingID, incomingSerializedMessage, p3 = os.pullEvent()
- if event == "rednet_message" then
- local unserializedMessage = textutils.unserialize(incomingSerializedMessage)
- -- Check if the two 'sender' and 'receiver' components of the message
- -- received exist; if so, then send the message; if not, throw it away.
- if unserializedMessage.receiver and unserializedMessage.sender then
- if not sendUnserializedMessage(unserializedMessage) then
- -- Save the message because it was not sent (the server did)
- -- not receive a confirmation message.
- saveUnsentMessage(unserializedMessage)
- end
- end
- elseif event == "timer" then
- -- If the timeout for listening has expired, then attempt to resend
- -- all unsent messages. Afterwards, restart the timer.
- if incomingID == listenTimer then
- resendUnsentMessages()
- -- If the timeout for updating the screen has expired, then
- -- redraw the current statistics on teh screen.
- elseif incomingID == updateTimer then
- drawMainScreen()
- end
- -- If the event was a mouse click, then check if we are to exit
- -- the program by returning false.
- elseif event == "mouse_click" then
- -- Check if the click was on the exit button.
- -- In this case, the incoming message will be the
- -- xPos for the click that occurred.
- if checkIfClickWasOnTextAtPos(incomingSerializedMessage, p3, exitOption.xPos, exitOption.yPos, exitOption.label) then
- return false
- end
- end
- -- Reset timers.
- listenTimer = os.startTimer(listenTimeout)
- updateTimer = os.startTimer(1)
- end
- end
- -- Rednet Functions --
- -- UI Functions --
- -- Clears the screen with the given color. If no color is provided, then black
- -- will be used instead.
- -- Params : color - The color to clear the screen with.
- -- Returns: nil
- function clearScreen(color)
- term.setBackgroundColor(color or colors.black)
- term.clear()
- term.setCursorPos(1, 1)
- end
- -- Prints the google logo to the position located. If no position was
- -- provided, then it will default to the top center of the screen.
- -- Params : xPos, yPos - The x and y coordinates for the logo to be drawn.
- -- Returns: nil
- function printLogo(xPos, yPos)
- if not xPos and not yPos then
- xPos = screenWidth/2 - ("Google"):len()/2
- yPos = 1
- end
- term.setCursorPos(xPos, yPos)
- local logoColors = {colors.blue, colors.red, colors.yellow, colors.blue,
- colors.green, colors.red} -- The colors for the logo
- -- that we're printing.
- local logo = "Google" -- The logo to be printed.
- for index = 1, logo:len() do
- term.setTextColor(logoColors[index])
- term.write(logo:sub(index, index))
- end
- end
- -- Prints the Gmail-Server title to the top of the screen at the given coordinates,
- -- or the center of the screen on line two by default.
- -- Params : xPos, yPos - The x and y coordinates of the title to be drawn.
- -- Returns: nil
- function printTitle(xPos, yPos)
- if not xPos and not yPos then
- xPos = screenWidth/2 - ("Gmail-Server"):len()/2
- yPos = 2
- end
- term.setCursorPos(xPos, yPos)
- local titleColors = {colors.blue, colors.red, colors.yellow, colors.blue,
- colors.green, colors.red, colors.blue, colors.red,
- colors.yellow, colors.blue, colors.green, colors.red}
- -- The colors for the title to be printed.
- local title = "Gmail-Server"
- for index = 1, title:len() do
- term.setTextColor(titleColors[index])
- term.write(title:sub(index, index))
- end
- end
- -- Prints the number of unsent messages to the screen. This is based off the
- -- size of the unsent messages directory.
- -- Params : xPos, yPos - The x and y coordinates for this information to be
- -- drawn at.
- -- Returns: nil
- function printNumberOfUnsentMessages(xPos, yPos)
- term.setCursorPos(xPos, yPos)
- term.write("Unsent Messages: " .. #fs.list(unsentMessagesDirectory))
- end
- -- Prints the number of messages that have been successfullt sent. This is
- -- based off of the 'sentMessagesCount' variable.
- -- Params : xPos, yPos - The x and y coordinates for this information to be
- -- drawn at.
- -- Returns: nil
- function printNumberOfSentMessages(xPos, yPos)
- term.setCursorPos(xPos, yPos)
- term.write("Sent Messages : " .. sentMessageCount)
- end
- -- Prints the current time in the Minecraft world. By default, however, this
- -- information will be written at the bottom right of the screen.
- -- Params : xPos, yPos - The x and y coordinates for this information to be
- -- drawn at.
- -- Returns: nil
- function printCurrentTimeInMinecraft(xPos, yPos)
- local time = textutils.formatTime(os.time(), true)
- if not xPos and not yPos then
- xPos = screenWidth/2 - ("Current Time: " .. time):len()/2
- yPos = screenHeight
- end
- term.setCursorPos(xPos, yPos)
- term.write("Current Time: " .. time)
- end
- -- Prints the exit option for the script.
- -- Params : nil
- -- Returns: nil
- function printExitOption()
- term.setCursorPos(exitOption.xPos, exitOption.yPos)
- local logoColors = {colors.blue, colors.red, colors.yellow, colors.blue,
- } -- The colors for the exit option that we're printing.
- for characterIndex = 1, exitOption.label:len() do
- term.setTextColor(logoColors[characterIndex])
- term.write(exitOption.label:sub(characterIndex, characterIndex))
- end
- -- Reset the colors.
- term.setTextColor(colors.black)
- end
- -- Draws the main screen for the server script.
- -- Params : nil
- -- Returns: nil
- function drawMainScreen()
- -- Clear the screen then print all of the necessary information,
- -- including the exit button.
- clearScreen(colors.white)
- printLogo()
- printTitle()
- printNumberOfUnsentMessages(2, 4)
- printNumberOfSentMessages(2, 5)
- printCurrentTimeInMinecraft()
- printExitOption()
- end
- -- UI Functions --
- -- Click Functions --
- -- Checks if the click at the given position is on top of the text at another
- -- given position provided.
- -- Params : xClickPos, yClickPos - The x and y coordinates of the click.
- -- xTextPos, yTextPos - The x and y coordinates of the text.
- -- text - The text to be checked for clicks on.
- -- Returns: true or false - Whether or not the click was on the text provided.
- function checkIfClickWasOnTextAtPos(xClickPos, yClickPos, xTextPos, yTextPos, text)
- if xClickPos >= xTextPos and xClickPos <= xTextPos + text:len() then
- if yClickPos == yTextPos then
- return true
- end
- end
- return false
- end
- -- Click Functions --
- -- File Functions --
- -- Stores the given message in the unsent messages directory. If there is a
- -- message with the same receiver/sender and the same subject, then this
- -- function will rename the message to the same thing with an appended count
- -- of the amount of messages with the same signatures. However, when this
- -- message is read from the file, it will be sent without the appended count.
- -- Params : message - The message table to be serialized and stored.
- -- Returns: nil
- function saveUnsentMessage(message)
- -- Generate a name for the new file and check it against the existing
- -- contents of the unsent messages directory in the case of a duplicate
- -- message.
- local unsentMessagePath = message.receiver .. '_' .. message.sender .. "_false"
- _, unsentMessagePath = checkForDuplicateFileInDirectory(unsentMessagesDirectory, unsentMessagePath)
- -- Get a file handle on the path for the new unsent message, then save
- -- the message.
- local unsentMessageFileHandle = fs.open(unsentMessagesDirectory .. '/' .. unsentMessagePath, 'w')
- unsentMessageFileHandle.write(textutils.serialize(message))
- unsentMessageFileHandle.close()
- end
- -- Checks for a file with the same name as the one provided.
- -- Params : directory, fileName - The path of the file to check the name of for
- -- duplicates/the directory to check.
- -- Returns: true, newFileName or false, fileName - The new file name in the case of
- -- a duplicate file name.
- function checkForDuplicateFileInDirectory(directory, fileName)
- local timesAppeared = getNumberOfTimesFileAppearsInDirectory(directory, fileName)
- if timesAppeared > 0 then
- local newName = fileName .. timesAppeared
- return true, newName
- end
- return false, fileName
- end
- -- Checks the amount of times the same file name appears in a directory.
- -- Params : directory, fileName - The directory and file name to check.
- -- Returns: timesAppeared - The number of times the same file name appears.
- function getNumberOfTimesFileAppearsInDirectory(directory, fileName)
- local timesAppeared = 0
- for index, item in pairs(fs.list(directory)) do
- if item:find(fileName) then
- timesAppeared = timesAppeared + 1
- end
- end
- return timesAppeared
- end
- -- Loads a message from the unsent messages directory and returns the message
- -- in its UNSERIALIZED form.
- -- Params : unsentMessageName - The name of the message to get from the directory.
- -- Returns: unsentMessage - The unsent message table.
- function loadUnsentMessageInUnserializedFormat(unsentMessageName)
- local unsentMessageFileHandle = fs.open(unsentMessagesDirectory .. '/' .. unsentMessageName, 'r')
- local unsentMessage = textutils.unserialize(unsentMessageFileHandle.readAll())
- unsentMessageFileHandle.close()
- return unsentMessage
- end
- -- Delets an unsent message.
- -- Params : unsentMessageName - The name of the message to delete.
- -- Returns: nil
- function deleteUnsentMessage(unsentMessageName)
- fs.delete(unsentMessagesDirectory .. '/' .. unsentMessageName)
- end
- -- File Functions --
- openOrCloseModem(true) -- Open any modem found on this computer.
- -- Setup all necessary directories properly if they have not been so already.
- if not fs.isDir("/Gmail-Server") then
- fs.makeDir("/Gmail-Server")
- end
- if not fs.isDir(unsentMessagesDirectory) then
- fs.makeDir(unsentMessagesDirectory)
- end
- -- Main Loop -------------------------------------------------------------------
- if not listenForIncomingMail() then
- openOrCloseModem(false)
- clearScreen(colors.black)
- term.setTextColor(colors.yellow)
- print(os.version())
- end
- --------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment