Bosschat
Spoiler
In-game chat too BORING for you? Or maybe playing SSP and you tend to forget things? Well be amazed, another person has written a chat program for you!Combining the most advanced programming techniques in the world with amazing good looks, code was born. Sporting exactly 2 features, Bosschat stands at 358 lines long. Leave yourself messages from the mines, write down directions to/from that lava pool you fell into, hoping your items are still there, and even ask yourself how your day is going.
Now in intermediate stage (No longer at basic stage).
Features (I guess)
- Is actually able to send and receive messages from other computers!
- Commands, commands, commands. Bosschat is able to handle commands. There are a few supported commands, /exit or /quit will exit BossChat, /name [username] will change your name from the compID to what you choose (Please Note: This code is so advanced that new usernames will only be in lowercase).
- A GUI more beautiful than finding diamonds that aren't inside/above lava pools.
- Global chatroom (more on this later)
- Backspacing!
- [BUGGED] It even tells you when you have caps lock enabled! (Warning: Pressing 'caps lock' too many times may cause death. Many subjects family members have since removed that key from their home PC's. BossChat Industries will not be held accountable for any damages to persons/objects that occur through repeated use of the 'caps lock' key.)
- Mute and unmute commands. Read the version history for more info.
- Automatic updates
- Tells in-game time
- Saves up to the most 30 recent messages in memory
- As of v.010 no longer shows if caps lock is on.
- If there are no computers already running BossChat, do not check for an update. Causes a program crash.
- If you break a released version of this program without editing it, I will mail you cookies everyday for life. Only applies to unknown bugs.
- Add message encryption (started)
- Private rooms (started)
- More commands (always looking for more)
- Dynamic modem placement (FINISHED. For v.005 - v.010 modem must be on right side)
- Local user database
Hold on to your seats, it's about to get real.
Programming is hard, I know that. Lua is different, I know that as well. I never learned Lua in school. My brother and I both weren't even sure this was a real programming language. But now I know it's real enough, anyway. I'm getting back into programming after a 3 year break (when I graduated high school) and this program is really for me, like a test. But at the same time, I'd be willing to help others who have no clue what their doing or are writing really inefficient code. If you feel like you need help programming and you also feel that my ability is sufficient enough to assist you, then by all means send me a PM. I try to respond…
Code (I am just a simple man)
Spoiler
local tArgs = {...}
local cursorY = 17
local username = tostring(os.computerID())
local cMessage = ""
local hMessage = ""
local charNum = tonumber(1)
local message = {"left", "right", "front", "back", "top", "bottom"}
local pastCount = 1
local pastMess = {}
local capOn = false
local room = "global"
local muted = false
local pVer = "011"
local tTimer = os.startTimer(1)
local terminate = false
local update = false
local pMessNum = 30
local modem = 0
if #tArgs < 1 or #tArgs > 2 then
print("Usage: bosschat <modem_side> <username(op)>")
terminate = true
end
if tArgs[1] ~= nil and tArgs[1] ~= "debug" then
for i = 1, 6 do
if tArgs[1] == message[i] then
rednet.open(message[i])
print("Opening modem on " .. tArgs[1])
else
modem = modem + 1
end
end
if modem == 6 then
print("Modem not found on " .. tArgs[1])
print("Program will close.")
terminate = true
end
end
if tArgs[2] ~= nil then
username = string.upper(string.sub(tArgs[2], 1, 1)) .. string.lower(string.sub(tArgs[2], 2))
end
for i = 1, 48 - (string.len(username) + 2) do --These two loops initialize message[] which is what you type and pastMess[i] which is a combination of messages you have received/sent.
message[i] = ""
end
for i = 1, pMessNum do
pastMess[i] = ""
end
function verCheck()
rednet.broadcast("UTIL v." .. pVer)
print("Detecting local clients.")
local id, message = rednet.receive(2)
if message == nil then
print("NOTE: No other users!")
sleep(3)
elseif tonumber(string.sub(message, 8)) > tonumber(pVer) then
print("Client found!")
print("Update required!")
sleep(3)
rednet.broadcast("UTIL READY")
if tonumber(pVer) < 10 then
pVer = "00" .. tonumber(pVer)
elseif tonumber(pVer) < 100 then
pVer = "0" .. tonumber(pVer)
end
print("Updating BossChat v." .. pVer .. " to v." .. string.sub(message,8))
shell.run("updater")
terminate = true
else
print("Client found!")
print("Client up-to-date!")
sleep(3)
--version = fs.open("version", "r")
--if version then
--local vID = version.readLine()
--version.close()
--end
--if tonumber(vID) >= tonumber(pVer) then
--ASK FOR UPDATE
--else
end
end
function cUpdater()
updater = fs.open("updater", "w")
if updater then
updater.writeLine([[rednet.open("right")]])
updater.writeLine([[rednet.broadcast("UPDATE")]])
updater.writeLine([[event, id, message = os.pullEvent("rednet_message")]])
updater.writeLine("")
updater.writeLine([[update = fs.open("bosschat", "w")]])
updater.writeLine("if update then")
updater.writeLine(" update.writeLine(message)")
updater.writeLine(" update.close()")
updater.writeLine("end")
updater.close()
end
end
function clearScreen()
term.clear()
term.setCursorPos(1,1)
end
function resetMessage() --Clears message[], same thing as inizialization. Called when pressing "enter".
for i = 1, 48 - (string.len(username) + 2) do
message[i] = ""
end
charNum = 1
end
function Time() --Tells current Minecraft time in the corner, bugged Caps Lock "feature"
term.setCursorPos(45,2)
if math.floor(os.time()) <= 9 then
term.write(" ")
end
term.write(textutils.formatTime(os.time(), true))
tTimer = os.startTimer(1)
end
function updatePast(newMess) --Updates the array pastMess[] and then calls updateScreen(). Might turn those two into a single function.
for i = 1, pMessNum - 1 do
pastMess[i] = pastMess[i + 1]
end
pastMess[pMessNum] = newMess
updateScreen()
end
function updateScreen() --Updates the screen with new messages.
if muted == false then
for i = 4, 15 do
term.setCursorPos(1, i)
term.clearLine()
end
for i = 4, 15 do
term.setCursorPos(1, i)
term.write("|")
term.setCursorPos(50, i)
term.write("|")
end
local counter = 0
for i = 12, 1, -1 do
term.setCursorPos(2, i + 3)
term.write(pastMess[pMessNum - counter])
counter = counter + 1
end
end
end
function Border() --Creates the space
clearScreen()
for i = 1, 50 do
term.setCursorPos(i, 1)
term.write("-")
term.setCursorPos(i, 3)
term.write("-")
term.setCursorPos(i, 16)
term.write("-")
term.setCursorPos(i, 18)
term.write("-")
end
for i = 1, 18 do
term.setCursorPos(1, i)
term.write("|")
term.setCursorPos(50, i)
term.write("|")
end
term.setCursorPos(2, 2)
term.write("BossChat v." .. pVer) --handled with version checking
end
function handleBackspace()
if charNum + 1 >= 3 then
charNum = charNum - 1
message[charNum] = " "
term.setCursorPos(charNum + string.len(username) + 3, cursorY)
term.write(" ")
term.setCursorPos(charNum + string.len(username) + 3, cursorY)
elseif charNum <= 1 then
charNum = 1
end
end
function handleSend(message) --Reads message[] letter by letter, saves it, clears the line and recreates the space. Then updates past messages and finally sends it.
for i = 1, charNum do
hMessage = hMessage .. message[i]
end
term.setCursorPos(1, 17)
term.clearLine()
term.setCursorPos(1, 17)
term.write("|")
term.setCursorPos(50, 17)
term.write("|")
if string.find(hMessage, "/") == 1 then
handleCommand(hMessage)
elseif hMessage ~= "" then
updatePast("[" .. username .. "]" ..hMessage)
messageSend(username, hMessage)
end
resetMessage()
return hMessage
end
function handleCaps(capOn) --Currently bugged
if capOn == false then
term.setCursorPos(20, 2)
term.write("CAPS ON")
capOn = true
elseif capOn == true then
term.setCursorPos(20, 2)
term.write(" ")
capOn = false
end
end
function handleCommand(command) --Handles all commands, easy to add new commands.
command = string.upper(command)
if string.sub(command, 1, 5) == "/EXIT" or string.sub(command, 1, 5) == "/QUIT" then
term.setCursorPos(2, 17)
term.write("Thank you for using BossChat")
for i = 1, 5 do
term.write(".")
sleep(0.0) --Took out exit delay
end
hMessage = ""
terminate = true
elseif string.sub(command, 1, 5) == "/NAME" then
hMessage = string.upper(string.sub(command, 7, 7)) .. string.lower(string.sub(command, 8))
updatePast("Name changed to " .. hMessage .. ".")
username = hMessage
elseif string.sub(command, 1, 5) == "/ROOM" then
room = string.sub(command, 7)
updatePast("Now speaking in " .. room .. ".")
elseif string.sub(command, 1, 5) == "/MUTE" then
updatePast("You have muted the room.")
muted = true
elseif string.sub(command, 1, 7) == "/UNMUTE" then
muted = false
updatePast("You have unmuted the room.")
elseif string.sub(command, 1, 6) == "/CLEAR" then
for i = 1, 12 do
pastMess[i] = ""
end
updateScreen()
else
updatePast(string.sub(command, 1, charNum) .. " currently does not exist.")
end
end
function writeChar(char) --Writes each character letter by letter and saves it in message[].
term.setCursorPos(charNum + string.len(username) + 3, cursorY)
if charNum <= 48 - (string.len(username) + 2) then
term.write(char)
message[charNum] = char
charNum = charNum + 1
end
end
function handleKey(key) --Handles misc. keys.
if key == 58 then
--handleCaps(capOn) --Bugged after using timers. Remove comments if you think you can make it work and send me edits to put in final version.
elseif key == 14 then
handleBackspace()
elseif key == 28 then
pMessNum = 30
hMessage = handleSend(message)
hMessage = ""
term.setCursorPos(2, 17)
term.write("[" .. username .. "]")
elseif key == 200 and pastMess[pMessNum - 1] ~= "" then --Moves messages up once, second check prevents moving higher than an empty message.
if pMessNum >= 13 then
pMessNum = pMessNum - 1
updateScreen(pMessNum)
end
elseif key == 208 then --Moves messages down once
if pMessNum <= 29 then
pMessNum = pMessNum + 1
updateScreen(pMessNum)
end
end
end
function handleTimers(timer) --Handles timers, currently only timer that updates clock exists.
if timer == tTimer then
Time()
end
end
function handleUtilities(id, message)
if string.sub(message, 6, 7) == "v." then
rednet.broadcast("UTIL v." .. pVer)
elseif string.sub(message, 6) == "READY" then
bosschat = fs.open("bosschat", "r")
if bosschat then
local update = bosschat.readAll()
bosschat.close()
rednet.send(id, update)
end
end
end
function messageReceive(id, message) --Lots of work needs to be done here...
if string.sub(message, 1, 4) == "UTIL" then
handleUtilities(id, message)
else
pMessNum = 30
updatePast(message)
end
end
function messageSend(username, hMessage) --LOADS of work needs to be done here.
rednet.broadcast(string.len(username) .. "e" .. "[" .. username .. "]" .. string.len(room) .. "e " .. room .. " " .. hMessage)
end
function handleEvent() --The main function that makes it all happen.
evt, p1, p2 = os.pullEvent()
if evt == "char" then
writeChar(p1)
elseif evt == "key" then
handleKey(p1)
elseif evt == "rednet_message" then
messageReceive(p1, p2)
elseif evt == "timer" then
handleTimers(p1)
end
end
if terminate == false then
clearScreen() --"Beginning" of program. (Visual stuff begins to happen here)
if tArgs[1] ~= "debug" then
cUpdater()
verCheck()
end
if terminate == true then
print("BossChat has been updated! Please restart the program.")
print("Press enter to continue")
io.read()
end
Border()
term.setCursorPos(2, 17)
term.write("[" .. username .. "]")
term.setCursorPos(2, 17)
updatePast("Speaking in " .. room .. ".")
Time()
end
while terminate == false do
handleEvent()
end
if #tArgs > 0 and modem ~= 6 then
clearScreen()
print("CraftOS 1.3")
end
Suggestions (God I love suggestions)
Please post suggestions in the comments and I will review them and most likely add the most advanced ones. But all suggestions are welcome.
Version History or something
- 6/13 v.011 Small-ish update. Haven't updated in a while so I figured I would. Major bug from last update fixed. Added true automatic updates. Program can accept parameters. Usage: bosschat <modem_side> <username (optional)>. Type 'debug' in place of a modem Small fix when scrolling upwards through past messages (program would scroll upwards through empty messages). Added /quit which does exactly the same as /exit because it uses the same code. Exiting BossChat no longer restarts your computer, just ends the program and simulates the screen as if it had been.
- 6/05 Major update released. Changes in spoiler:
Spoiler
Okay, not exactly an auto-updater (due to problems with extremely advanced code), but an updater has been released. To update, simply run BossChat on a computer with a lower Version Identifier (or something) and enter 'Y' or 'y' when prompted. Should always update okay. Make sure your modems are on the right side. NOTE: Only for clients v.010 and later. ALSO NOTE: This version is BUGGED. If there are no other clients and you attempt to update, it will timeout and return with an error. SO DON'T DO THAT (type 'n' and hit enter). I think I found a way around this, but I'm done coding for tonight. If anybody reads this far down and knows how to check for a string being equal to nil, please tell.FOR A COMPUTER WITHOUT BOSSCHAT: You need to have the updater. Here it is. Highly versatile, can be used to transfer other files with a pinch of editing. Make sure you change "bosschat" to something else if you decide to edit. Don't want to overwrite it.
Spoiler
rednet.open("right")
rednet.broadcast("READY")
event, id, message = os.pullEvent("rednet_message")
update = fs.open("bosschat", "w")
if update then
update.writeLine(message)
update.close()
end
Just run this program after BossChat is running on the computer you want to update FROM.
Programming advances have allowed the addition of a clock.
Now with time travel! Bosschat now saves up to 30 messages (including Command printouts). Press the up arrow to scroll back in time and down arrow to return to the present! If you send or receive a message while looking at past messages, BossChat will automatically scroll to the bottom. This is considered a feature, but it is understandable how this could get annoying. Can be easily edited to save more messages due to advanced programming techniques.
Left debug data for private rooms in sent messages. You'll see what I mean when you run. Adding those headers will help when I inevitably finish this project and work on networking so this can all come together on a giant server (that someone else will host (my computer is ass)).
Bunch of small stuff that no one will notice/care about.
- 5/31 Major unreleased update to code.
- 5/30 Added '/mute' and '/unmute' commands. Mute will keep your computer from updating the screen, although the messages will still be saved. Unmute will allow your computer to start updating the screen again and will display as many messages as possible that you missed (There is currently a maximum of 12 saved messages, and subtract one. Thank Teraminer for the idea.
- 5/29 First official release BossChat v.005. Came up with name BossChat on the fly.
- Pre-5/29 Rewrote code twice, making it more advanced each time. Used third rewrite as the base for BossChat v.005
Spoiler
Special thanks to Max96at!https://dl.dropbox.c...cs/bawsChat.png
Special thanks to thesbros for all of these!
https://dl.dropbox.c...bosschat-3d.png
https://dl.dropbox.c...schat-gears.png
https://dl.dropbox.c...at-gradient.png
https://dl.dropbox.c...schat-rocky.png
https://dl.dropbox.c...bosschat-mc.png
https://dl.dropbox.c...osschat-idk.png
Spoiler
Here's a bit of code I was working on, the reason I took a break from BossChat for a few days was to get familiar with having programs accept arguments during run-time. Result can be seen in the 6/13 update for BossChat, anyway here's the code for a program that will tell you how many of each item you can create with certain materials. Type 'recipe' for arguments used and type 'recipe <item>' to see which arguments are accepted. I'm fed up with this code, will not be updating it, and anyone that is willing to try to use it is welcome to as long as they write that they got the base from me.Working items:
- Dispensers
- Noteblocks
- Powered Rails (untested)
- Detector Rails (untested, unfinished and buggy)
- Signs
- Everything not listed above
Spoiler
local tArgs = { ... }
local rItem = {" ", " ", " ", " ", " ", " ", " ", " ", " "}
local create = 0
local cItem = 0
local rec = ""
term.clear()
term.setCursorPos(1,1)
if #tArgs < 1 then
print("Usage: recipe <item> <material 1> <mat 2> etc")
print("Usage: recipe reverse <amount> <item>")
return
end
function craft(rItem)
print(" - - -")
print("|" .. rItem[1] .. "|" .. rItem[2] .. "|" .. rItem[3] .. "|")
print(" - - -")
print("|" .. rItem[4] .. "|" .. rItem[5] .. "|" .. rItem[6] .. "|")
print(" - - -")
print("|" .. rItem[7] .. "|" .. rItem[8] .. "|" .. rItem[9] .. "|")
print(" - - -")
end
local item = tArgs[1]
if item == "dispenser" then
if #tArgs ~= 4 then
print("Usage: recipe dispenser <bow> <cobblestone> <redstone>")
else
bow = tArgs[2]
cobblestone = tArgs[3]
redstone = tArgs[4]
while tonumber(bow) > 0 and tonumber(cobblestone) > 6 and tonumber(redstone) > 0 do --Remove tonumber() to see why I am finished with this code.
bow = bow - 1
cobblestone = cobblestone - 7
redstone = redstone - 1
cItem = cItem + 1
end
print("Dispensers: " .. cItem)
if bow == 0 and cobblestone == 0 and redstone == 0 then
else
print("")
print("Materials left:")
print("Bows: " .. bow)
print("Cobblestone: " .. cobblestone)
print("Redstone: " .. redstone)
end
print("Need recipe? (Y/N)")
rec = io.read()
if string.upper(rec) == "Y" then
for i = 1, 9 do
if i ~= 5 or i ~= 8 then
rItem[i] = "c"
end
end
rItem[5] = "b"
rItem[8] = "r"
craft(rItem)
print("c = cobblestone")
print("b = bow")
print("r = redstone")
end
end
elseif item == "noteblock" then
if #tArgs ~= 2 then
print("Usage: recipe noteblock <wood> <plank> <redstone>")
else
plank = tArgs[3] + (tArgs[2] * 4)
redstone = tArgs[4]
while plank > 7 and redstone > 0 do
plank = plank - 8
redstone = redstone - 1
cItem = cItem + 1
end
print("Noteblocks: " .. cItem)
if plank == 0 and redstone == 0 then
else
print("Planks: " .. plank)
print("Redstone: " .. redstone)
end
print("Need recipe? (Y/N)")
rec = io.read()
if string.upper(rec) == "Y" then
for i = 1, 9 do
if i ~= 5 then
rItem[i] = "p"
end
end
rItem[5] = "r"
craft(rItem)
print("p = plank")
print("r = redstone")
end
end
elseif item == "powered_rail" then
if #tArgs ~= 6 then
print("Usage: recipe sign <wood> <plank> <stick> <gold> <redstone>")
else
plank = tArgs[3] + (tArgs[2] * 4)
stick = tArgs[4]
gold = tArgs[5]
redstone = tArgs[6]
while stick > 0 and redstone > 0 and gold > 5 do
if stick < 2 and plank > 1 then
plank = plank - 2
stick = stick + 4
create = create + 1
end
gold = gold - 6
stick = stick - 1
redstone = redstone - 1
cItem = cItem + 1
end
print("Powered Rails: " .. cItem)
if create ~= 0 then
print("")
print("You will need to create: " .. create *4 .. " sticks.")
end
if gold == 0 and redstone == 0 and stick == 0 then
else
print("Gold: " .. gold)
print("Redstone: " .. redstone)
print("Stick: " .. stick)
end
print("Need recipe? (Y/N)")
rec = io.read()
if string.upper(rec) == "Y" then
for i = 1, 9 do
if i ~= 2 and i ~= 5 and i ~= 8 then
rItem[i] = "g"
end
end
rItem[5] = "s"
rItem[8] = "r"
craft(rItem)
print("g = gold")
print("r = redstone")
print("s = stick")
end
end
elseif item == "detector_rail" then
if #tArgs ~= 4 and #tArgs ~= 5 then
print("Usage: recipe detector_rail <iron> <redstone> <pressure_plate>")
print("Optional usage: recipe etc. <pressure_plate> <cobblestone>")
else
iron = tArgs[2]
redstone = tArgs[3]
pressplate = tArgs[4]
cobblestone = tArgs[5]
if pressplate == 0 and cobblestone >= 4 then
cobblestone = cobblestone - 4
pressplate = pressplate + 2
create = create + 2
end
while tonumber(iron) > 5 and tonumber(redstone) > 0 and tonumber(pressplate) > 0 do --remove tonumber() to see dysfunctional code in action.
if tonumber(pressplate) < 2 and tonumber(tArgs[5]) ~= nil and tonumber(cobblestone) > 1 then --remove tonumber() to see why every table in my house has been flipped.
cobblestone = cobblestone - 2
pressplate = pressplate + 1
create = create + 1
end
iron = iron - 6
redstone = redstone - 1
pressplate = pressplate - 1
cItem = cItem + 1
end
print("Detector Rails: " .. cItem)
if create ~= 0 then
print("")
print("You will need to create: " .. create .. " pressure plates.")
end
if iron == 0 and redstone == 0 and pressplate == 0 then
else
print("Iron: " .. iron)
print("Redstone: " .. redstone)
print("Pressure Plates: " .. pressplate)
end
end
elseif item == "sign" then
if #tArgs ~= 4 then
print("Usage: recipe sign <wood> <plank> <stick>")
else
plank = tArgs[3] + (tArgs[2] * 4)
stick = tArgs[4]
print("Calculating")
while tonumber(plank) >= 6 do
if stick == 0 and plank > 7 then
plank = plank - 2
stick = stick + 4
create = create + 1
end
plank = plank - 6
stick = stick - 1
cItem = cItem + 1
end
print("Signs: " .. cItem)
if create ~= 0 then
print("")
print("You will need to create: " .. create * 4 .. " sticks.")
end
if plank == 0 and stick == 0 then
else
print("")
print("Materials left:")
print("Planks: " .. plank)
print("Sticks: " .. stick)
end
end
end