diff --git a/client/Libs/Utils.lua b/client/Libs/Utils.lua new file mode 100644 index 0000000..5b2b486 --- /dev/null +++ b/client/Libs/Utils.lua @@ -0,0 +1,797 @@ +-- os Additions +function os.getSystemBit() + if (os.getenv('PROCESSOR_ARCHITEW6432')=='AMD64' or os.getenv('PROCESSOR_ARCHITECTURE')=='AMD64') then + return 64 + else + return 32 + end +end +function os.sleep(n) + if not n then n=0 end + local t0 = os.clock() + while os.clock() - t0 <= n do end +end +function os.pause(msg) + if msg ~= nil then + print(msg) + end + io.read() +end +function os.batCmd(cmd) + io.mkFile('temp.bat',cmd) + local temp = os.execute([[temp.bat]]) + io.delFile('temp.bat') + return temp +end +function os._getOS() + if package.config:sub(1,1)=='\\' then + return 'windows' + else + return 'unix' + end +end +function os.getOS(t) + if not t then + return os._getOS() + end + if os._getOS()=='unix' then + fh,err = io.popen('uname -o 2>/dev/null','r') + if fh then + osname = fh:read() + end + if osname then return osname end + end + local winver='Unknown Version' + local a,b,c=os.capture('ver'):match('(%d+).(%d+).(%d+)') + local win=a..'.'..b..'.'..c + if type(t)=='string' then + win=t + end + if win=='4.00.950' then + winver='95' + elseif win=='4.00.1111' then + winver='95 OSR2' + elseif win=='4.00.1381' then + winver='NT 4.0' + elseif win=='4.10.1998' then + winver='98' + elseif win=='4.10.2222' then + winver='98 SE' + elseif win=='4.90.3000' then + winver='ME' + elseif win=='5.00.2195' then + winver='2000' + elseif win=='5.1.2600' then + winver='XP' + elseif win=='5.2.3790' then + winver='Server 2003' + elseif win=='6.0.6000' then + winver='Vista/Windows Server 2008' + elseif win=='6.0.6002' then + winver='Vista SP2' + elseif win=='6.1.7600' then + winver='7/Windows Server 2008 R2' + elseif win=='6.1.7601' then + winver='7 SP1/Windows Server 2008 R2 SP1' + elseif win=='6.2.9200' then + winver='8/Windows Server 2012' + elseif win=='6.3.9600' then + winver='8.1/Windows Server 2012' + elseif win=='6.4.9841' then + winver='10 Technical Preview 1' + elseif win=='6.4.9860' then + winver='10 Technical Preview 2' + elseif win=='6.4.9879' then + winver='10 Technical Preview 3' + elseif win=='10.0.9926' then + winver='10 Technical Preview 4' + end + return 'Windows '..winver +end +function os.getLuaArch() + return (#tostring({})-7)*4 +end +if os.getOS()=='windows' then + function os.sleep(n) + if n > 0 then os.execute('ping -n ' .. tonumber(n+1) .. ' localhost > NUL') end + end +else + function os.sleep(n) + os.execute('sleep ' .. tonumber(n)) + end +end +function os.capture(cmd, raw) + local f = assert(io.popen(cmd, 'r')) + local s = assert(f:read('*a')) + f:close() + if raw then return s end + s = string.gsub(s, '^%s+', '') + s = string.gsub(s, '%s+$', '') + s = string.gsub(s, '[\n\r]+', ' ') + return s +end +function os.getCurrentUser() + return os.getenv('$USER') or os.getenv('USERNAME') +end +-- string Additions +function string.trim(s) + local from = s:match"^%s*()" + return from > #s and "" or s:match(".*%S", from) +end +function string.random(n) + local str = '' + strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'} + for i=1,n do + h = math.random(1,#strings) + str = str..''..strings[h] + end + return str +end +function string.linesToTable(s) + local t = {} + local i = 0 + while true do + i = string.find(s, '\n', i+1) + if i == nil then return t end + table.insert(t, i) + end +end +function string.lines(str) + local t = {} + local function helper(line) table.insert(t, line) return '' end + helper((str:gsub('(.-)\r?\n', helper))) + return t +end +function string.split(str, pat) + local t = {} -- NOTE: use {n = 0} in Lua-5.0 + local fpat = '(.-)' .. pat + local last_end = 1 + local s, e, cap = str:find(fpat, 1) + while s do + if s ~= 1 or cap ~= '' then + table.insert(t,cap) + end + last_end = e+1 + s, e, cap = str:find(fpat, last_end) + end + if last_end <= #str then + cap = str:sub(last_end) + table.insert(t, cap) + end + return t +end +function string.shuffle(inputStr) + math.randomseed(os.time()); + local outputStr = ''; + local strLength = string.len(inputStr); + while (strLength ~=0) do + local pos = math.random(strLength); + outputStr = outputStr..string.sub(inputStr,pos,pos); + inputStr = inputStr:sub(1, pos-1) .. inputStr:sub(pos+1); + strLength = string.len(inputStr); + end + return outputStr; +end +function string.genKeys(chars,a,f,s,GG) + if GG then + chars=string.rep(chars,a) + end + if s then + chars=string.shuffle(chars) + end + b=#chars + if a==0 then return end + local taken = {} local slots = {} + for i=1,a do slots[i]=0 end + for i=1,b do taken[i]=false end + local index = 1 + local tab={} + for i=1,#chars do + table.insert(tab,chars:sub(i,i)) + end + while index > 0 do repeat + repeat slots[index] = slots[index] + 1 + until slots[index] > b or not taken[slots[index]] + if slots[index] > b then + slots[index] = 0 + index = index - 1 + if index > 0 then + taken[slots[index]] = false + end + break + else + taken[slots[index]] = true + end + if index == a then + local tt={} + for i=1,a do + table.insert(tt,tab[slots[i]]) + end + f(table.concat(tt)) + taken[slots[index]] = false + break + end + index = index + 1 + until true end +end +-- io Additions +function io.getInput(msg) + if msg ~= nil then + io.write(msg) + end + return io.read() +end +function io.scanDir(directory) + directory=directory or io.getDir() + local i, t, popen = 0, {}, io.popen + if os.getOS()=='unix' then + for filename in popen('ls -a \''..directory..'\''):lines() do + i = i + 1 + t[i] = filename + end + else + for filename in popen('dir \''..directory..'\' /b'):lines() do + i = i + 1 + t[i] = filename + end + end + return t +end +function io.buildFromTree(tbl, indent,folder) + if not indent then indent = 0 end + if not folder then folder = '' end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ':' + if type(v) == 'table' then + if not(io.dirExists(folder..string.sub(formatting,1,-2))) then + io.mkDir(folder..string.sub(formatting,1,-2)) + end + io.buildFromTree(v,0,folder..string.sub(formatting,1,-2)..'\\') + else + a=string.find(tostring(v),':',1,true) + if a then + file=string.sub(tostring(v),1,a-1) + data=string.sub(tostring(v),a+1) + io.mkFile(folder..file,data,'wb') + else + io.mkFile(folder..v,'','wb') + end + end + end +end +function io.cpFile(path,topath) + if os.getOS()=='unix' then + os.execute('cp '..file1..' '..file2) + else + os.execute('Copy '..path..' '..topath) + end +end +function io.delDir(directoryname) + if os.getOS()=='unix' then + os.execute('rm -rf '..directoryname) + else + os.execute('rmdir '..directoryname..' /s /q') + end +end +function io.delFile(path) + os.remove(path) +end +function io.mkDir(dirname) + os.execute('mkdir "' .. dirname..'"') +end +function io.mkFile(filename,data,tp) + if not(tp) then tp='wb' end + if not(data) then data='' end + file = io.open(filename, tp) + if file==nil then return end + file:write(data) + file:close() +end +function io.movFile(path,topath) + io.cpFile(path,topath) + io.delFile(path) +end +function io.listFiles(dir) + if not(dir) then dir='' end + local f = io.popen('dir \''..dir..'\'') + if f then + return f:read('*a') + else + print('failed to read') + end +end +function io.getDir(dir) + if not dir then return io.getWorkingDir() end + if os.getOS()=='unix' then + return os.capture('cd '..dir..' ; cd') + else + return os.capture('cd '..dir..' & cd') + end +end +function io.getWorkingDir() + return io.popen'cd':read'*l' +end +function io.fileExists(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +function io.fileCheck(file_name) + if not file_name then print('No path inputed') return false end + local file_found=io.open(file_name, 'r') + if file_found==nil then + file_found=false + else + file_found=true + end + return file_found +end +function io.dirExists(strFolderName) + strFolderName = strFolderName or io.getDir() + local fileHandle, strError = io.open(strFolderName..'\\*.*','r') + if fileHandle ~= nil then + io.close(fileHandle) + return true + else + if string.match(strError,'No such file or directory') then + return false + else + return true + end + end +end +function io.getAllItems(dir) + local t=os.capture("cd \""..dir.."\" & dir /a-d | find",true):lines() + return t +end +function io.listItems(dir) + if io.dirExists(dir) then + temp=io.listFiles(dir) -- current directory if blank + if io.getDir(dir)=='C:\\\n' then + a,b=string.find(temp,'C:\\',1,true) + a=a+2 + else + a,b=string.find(temp,'..',1,true) + end + temp=string.sub(temp,a+2) + list=string.linesToTable(temp) + temp=string.sub(temp,1,list[#list-2]) + slist=string.lines(temp) + table.remove(slist,1) + table.remove(slist,#slist) + temp={} + temp2={} + for i=1,#slist do + table.insert(temp,string.sub(slist[i],40,-1)) + end + return temp + else + return nil + end +end +function io.getDirectories(dir,l) + if dir then + dir=dir..'\\' + else + dir='' + end + local temp2=io.scanDir(dir) + for i=#temp2,1,-1 do + if io.fileExists(dir..temp2[i]) then + table.remove(temp2,i) + elseif l then + temp2[i]=dir..temp2[i] + end + end + return temp2 +end +function io.getFiles(dir,l) + if dir then + dir=dir..'\\' + else + dir='' + end + local temp2=io.scanDir(dir) + for i=#temp2,1,-1 do + if io.dirExists(dir..temp2[i]) then + table.remove(temp2,i) + elseif l then + temp2[i]=dir..temp2[i] + end + end + return temp2 +end +function io.getFullName(name) + local temp=name or arg[0] + if string.find(temp,'\\',1,true) or string.find(temp,'/',1,true) then + temp=string.reverse(temp) + a,b=string.find(temp,'\\',1,true) + if not(a) or not(b) then + a,b=string.find(temp,'/',1,true) + end + return string.reverse(string.sub(temp,1,b-1)) + end + return temp +end +function io.getName(file) + local name=io.getFullName(file) + name=string.reverse(name) + a,b=string.find(name,'.',1,true) + name=string.sub(name,a+1,-1) + return string.reverse(name) +end +function io.readFile(file) + local f = io.open(file, 'rb') + local content = f:read('*all') + f:close() + return content +end +function io.getExtension(file) + local file=io.getFullName(file) + file=string.reverse(file) + local a,b=string.find(file,'.',0,true) + local temp=string.sub(file,1,b) + return string.reverse(temp) +end +function io.pathToTable(path) + local p=io.splitPath(path) + local temp={} + temp[p[1]]={} + local last=temp[p[1]] + for i=2,#p do + snd=last + last[p[i]]={} + last=last[p[i]] + end + return temp,last,snd +end +function io.splitPath(str) + return string.split(str,'[\\/]+') +end + +function io.parseDir(dir,t) + io.tempFiles={} + function _p(dir) + local dirs=io.getDirectories(dir,true) + local files=io.getFiles(dir,true) + for i=1,#files do + p,l,s=io.pathToTable(files[i]) + if t then + s[io.getFullName(files[i])]=io.readFile(files[i]) + else + s[io.getFullName(files[i])]=io.open(files[i],'r+') + end + table.merge(io.tempFiles,p) + end + for i=1,#dirs do + table.merge(io.tempFiles,io.pathToTable(dirs[i])) + _p(dirs[i],t) + end + end + _p(dir) + return io.tempFiles +end +function io.parsedir(dir,f) + io.tempFiles={} + function _p(dir,f) + local dirs=io.getDirectories(dir,true) + local files=io.getFiles(dir,true) + for i=1,#files do + if not f then + table.insert(io.tempFiles,files[i]) + else + f(files[i]) + end + end + for i=1,#dirs do + _p(dirs[i],f) + end + end + _p(dir,f) + return io.tempFiles +end +function io.driveReady(drive) + drive=drive:upper() + if not(drive:find(':',1,true)) then + drive=drive..':' + end + drives=io.getDrives() + for i=1,#drives do + if drives[i]==drive then + return true + end + end + return false +end +function io.getDrives() + if os.getOS()=='windows' then + local temp={} + local t1=os.capture('wmic logicaldisk where drivetype=2 get deviceid, volumename',true) + local t2=os.capture('wmic logicaldisk where drivetype=3 get deviceid, volumename',true) + for drive,d2 in t1:gmatch('(.:)%s-(%w+)') do + if #d2>1 then + table.insert(temp,drive) + end + end + for drive in t2:gmatch('(.:)') do + table.insert(temp,drive) + end + return temp + end + error('Command is windows only!') +end +-- table Additions +function table.dump(t,indent) + local names = {} + if not indent then indent = '' end + for n,g in pairs(t) do + table.insert(names,n) + end + table.sort(names) + for i,n in pairs(names) do + local v = t[n] + if type(v) == 'table' then + if(v==t) then + print(indent..tostring(n)..': <-') + else + print(indent..tostring(n)..':') + table.dump(v,indent..' ') + end + else + if type(v) == 'function' then + print(indent..tostring(n)..'()') + else + print(indent..tostring(n)..': '..tostring(v)) + end + end + end +end +function table.alphanumsort(o) + local function padnum(d) local dec, n = string.match(d, '(%.?)0*(.+)') + return #dec > 0 and ('%.12f'):format(d) or ('%s%03d%s'):format(dec, #n, n) + end + table.sort(o, function(a,b) return tostring(a):gsub('%.?%d+',padnum)..('%3d'):format(#b)< tostring(b):gsub('%.?%d+',padnum)..('%3d'):format(#a) end) + return o +end +function table.foreach(t,f) + for i,v in pairs(t) do + f(v) + end +end +function table.merge(t1, t2) + for k,v in pairs(t2) do + if type(v) == 'table' then + if type(t1[k] or false) == 'table' then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +function table.print(tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ': ' + if type(v) == 'table' then + print(formatting) + table.print(v, indent+1) + else + print(formatting .. tostring(v)) + end + end +end +function table.merge(t1, t2) + for k,v in pairs(t2) do + if type(v) == 'table' then + if type(t1[k] or false) == 'table' then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +function table.clear(t) + for k in pairs (t) do + t[k] = nil + end +end +function table.copy(t) + function deepcopy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + setmetatable(copy, deepcopy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy + end + return deepcopy(t) +end +function table.swap(tab,i1,i2) + tab[i1],tab[i2]=tab[i2],tab[i1] +end +function table.append(t1, ...) + t1,t2= t1 or {},{...} + for k,v in pairs(t2) do + t1[#t1+1]=t2[k] + end + return t1 +end +function table.compare(t1, t2,d) + if d then + return table.deepCompare(t1,t2) + end + --if #t1 ~= #t2 then return false end + if #t2>#t1 then + for i=1,#t2 do + if t1[i] ~= t2[i] then + return false,t2[i] + end + end + else + for i=1,#t1 do + if t1[i] ~= t2[i] then + return false,t2[i] + end + end + end + return true +end +function table.deepCompare(t1,t2) + if t1==t2 then return true end + if (type(t1)~='table') then return false end + local mt1 = getmetatable(t1) + local mt2 = getmetatable(t2) + if( not table.deepCompare(mt1,mt2) ) then return false end + for k1,v1 in pairs(t1) do + local v2 = t2[k1] + if( not table.deepCompare(v1,v2) ) then return false end + end + for k2,v2 in pairs(t2) do + local v1 = t1[k2] + if( not table.deepCompare(v1,v2) ) then return false end + end + return true +end +function table.has(t,_v) + for i,v in pairs(t) do + if v==_v then + return true + end + end + return false +end +function table.reverse(tab) + local size = #tab + local newTable = {} + for i,v in ipairs (tab) do + newTable[size-i] = v + end + for i=1,#newTable do + tab[i]=newTable[i] + end +end +-- Math Additions +local Y = function(g) local a = function(f) return f(f) end return a(function(f) return g(function(x) local c=f(f) return c(x) end) end) end +local F = function(f) return function(n)if n == 0 then return 1 else return n*f(n-1) end end end +math.factorial = Y(F) +math.fib={} +math.fib.fibL={} +setmetatable(math.fib,{__call=function(self,n) + if n<=2 then + return 1 + else + if self.fibL[n] then + return self.fibL[n] + else + local t=math.fib(n-1)+math.fib(n-2) + self.fibL[n]=t + return t + end + end +end}) +local floor,insert = math.floor, table.insert +function math.basen(n,b) + n = floor(n) + if not b or b == 10 then return tostring(n) end + local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + local t = {} + local sign = '' + if n < 0 then + sign = '-' + n = -n + end + repeat + local d = (n % b) + 1 + n = floor(n / b) + insert(t, 1, digits:sub(d,d)) + until n == 0 + return sign .. table.concat(t,'') +end +function math.convbase(n,b,tb) + return math.basen(tonumber(tostring(n),b),tb) +end +if BigNum then + function BigNum.mod(a,b) + return a-((a/b)*b) + end + local floor,insert = math.floor, table.insert + function math.basen(n,b) + n = BigNum.new(n) + if not b or b == 10 then return tostring(n) end + local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + local t = {} + local sign = '' + if n < BigNum.new(0) then + sign = '-' + n = -n + end + repeat + local d = BigNum.mod(n , b) + 1 + n = n/b + d=tonumber(tostring(d)) + insert(t, 1, digits:sub(d,d)) + until tonumber(tostring(n)) == 0 + return sign .. table.concat(t,'') + end + function math.to10(n,b) + local num=tostring(n) + local sum=BigNum.new() + local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + for i=1,#num do + local v=digits:find(num:sub(i,i),1,true) + sum=sum+BigNum.new(tonumber(v)-1)*BigNum.pow(BigNum.new(b),BigNum.new(#num-i)) + end + return sum + end + function math.convbase(n,b,tb) + return math.basen(math.to10(n,b),tb) + end +end +function math.numfix(n,x) + local str=tostring(n) + if #str255 then self.C1=254 self.C1m=-1 end + if self.C2>255 then self.C2=254 self.C2m=-1 end + if self.C3>255 then self.C3=254 self.C3m=-1 end + if self.C1<0 then self.C1=1 self.C1m=1 end + if self.C2<0 then self.C2=1 self.C2m=1 end + if self.C3<0 then self.C3=1 self.C3m=1 end + self.Color=Color.new(self.C1,self.C2,self.C3) +end) +function love.wheelmoved(x, y) -- handle scrolling + if mainapp then else return end + if y > 0 then + Chat.bubbles:SetDualDim(nil,Chat.bubbles.y+10) + elseif y < 0 then + Chat.bubbles:SetDualDim(nil,Chat.bubbles.y-50) + end + if Chat.bubbles.offset.pos.y>0 then + Chat.bubbles.offset.pos.y=0 + end + print(Chat.bubbles.offset.pos.y) +end +gui.ff:OnUpdate(func) function InitEngine() mainapp=gui:newFrame("",0,0,0,0,0,0,1,1) -- creates a frame that takes up the enitre window - mainapp.header=mainapp:newTextLabel("Chat Program",0,0,0,20,0,0,1) -- creates the header for the app - mainapp.header.Tween=-3 -- moves the text up by a factor of 3 pixels + mainapp.Color=Color.Lighten(Color.Brown,.25) + mainapp.BorderSize=0 + mainapp.header=mainapp:newTextLabel("Now chatting as: "..MY_Name,1,-10,-2,30,0,0,1) -- creates the header for the app + mainapp.header:setRoundness(15,15,360) + mainapp.header.Tween=7 -- moves the text up by a factor of 3 pixels mainapp.header.Color=Color.Lighten(Color.Blue,.25) -- Sets the Color of the header object to light blue - mainapp.chatFrame=mainapp:newTextLabel("",0,20,0,-20,0,0,1,1) -- creates the chat frame where users can see the text that is sent - mainapp.chatFrame.Color=Color.Lighten(Color.Brown,.15) -- Color stuff - mainapp.chatFrame.Tween=-3 -- text positioning - mainapp.chatFrame.TextFormat="left" -- changes the text format to left hand side of screen + mainapp.chatFrame=mainapp:newFrame("",0,21,0,-20,0,0,1,1) -- creates the chat frame where users can see the text that is sent + mainapp.chatFrame.Color=Color.Lighten(Color.Brown,.25) -- Color stuff + mainapp.chatFrame.ClipDescendants=true + mainapp.chatFrame.bubbles=mainapp.chatFrame:newFrame("",0,0,0,0,0,0,1) + mainapp.chatFrame.bubbles.Visibility=0 + Chat=mainapp.chatFrame + Chat.lastBubbleHeight=0 + function Chat:AddChatBubble(user,txt,isself) + local msg=user..": "..txt + local bubble=self.bubbles:newTextLabel(msg,0,self.lastBubbleHeight+5,math.floor(mainapp.width/2),(math.ceil(#msg/24)*12)+5) + bubble.TextFormat="left" + bubble.XTween=2 + bubble.Tween=-4 + bubble:setRoundness(5,5,360) + if isself then + bubble:anchorRight(1) + end + self.lastBubbleHeight=self.lastBubbleHeight+(math.ceil(#msg/24)*12)+13 + if self.lastBubbleHeight>mainapp.height-60 then + self.bubbles.offset.pos.y=-(self.lastBubbleHeight-(mainapp.height-60)) + end + end mainapp.textbox=mainapp:newTextBox("",5,-35,-10,30,0,1,1) -- creates a textbox that allows the user to be able to send messages mainapp.textbox.TextFormat="left" -- sets the format to left hand side of screen mainapp.textbox.Color=Color.tan mainapp.textbox:setRoundness(5,5,360) mainapp.textbox:OnEnter(function(self,txt) -- handles the send event - client:sendChat("NAMENOSPACE",txt) -- sends the data to the server + client:sendChat(MY_Name,txt) -- sends the data to the server self.text="" self.Color=Color.tan end) @@ -38,5 +110,4 @@ function InitEngine() mainapp.textbox.ClearOnFocus=true mainapp.textbox.XTween=2 mainapp.textbox.Tween=2 -- positions the text in the text box - --This displays the chatting frame and allows connection to the server... Look at server.lua to see the servers code end diff --git a/client/net/chatting.lua b/client/net/chatting.lua index 09393df..74f58f3 100644 --- a/client/net/chatting.lua +++ b/client/net/chatting.lua @@ -21,7 +21,14 @@ function net.chatting:init() -- calling this initilizes the library and binds it msg=msg } self.OnChatRecieved:Fire(struct) -- trigger the chat event - self:sendAll("!chatting! "..struct.user.." '"..struct.msg.."'") + for i,v in pairs(self.ips) do + if ip==v then + print("Self: "..struct.user) + self:send(v,"!chatting! 1 "..struct.user.." '"..struct.msg.."'") + else + self:send(v,"!chatting! 0 "..struct.user.." '"..struct.msg.."'") + end + end end end) s.rooms={} @@ -34,10 +41,11 @@ function net.chatting:init() -- calling this initilizes the library and binds it net.OnClientCreated:connect(function(c) c.OnDataRecieved:connect(function(self,data) -- when the client recieves data this method is triggered --First Lets make sure we are getting chatting data - local user,msg = data:match("!chatting! (%S-) '(.+)'") + local isself, user,msg = data:match("!chatting! (%d) (%S-) '(.+)'") if user and msg then --This is the client so our job here is done - self.OnChatRecieved:Fire(user,msg) -- trigger the chat event + print(isself) + self.OnChatRecieved:Fire(user,msg,({["1"]=true, ["0"]=false})[isself]) -- trigger the chat event end end) function c:sendChat(user,msg) diff --git a/client/net/email.lua b/client/net/email.lua new file mode 100644 index 0000000..18de805 --- /dev/null +++ b/client/net/email.lua @@ -0,0 +1,53 @@ +require("net.identity") +net:registerModule("email",{1,0,0}) +smtp = require 'socket.smtp' +ssl = require 'ssl' + +function net.email.init(from,user,pass) + net.OnServerCreated:connect(function(s) + s.from=from + s.user=user + s.pass=pass + function s:sendMessage(subject, body, dTable) + local msg = { + headers = { + from = '<'..dTable.email..'>' + to = dTable.nick..' <'..dTable.email..'>', + subject = subject + }, + body = body + } + local ok, err = smtp.send { + from = '<'..self.from..'>', + rcpt = '<'..dTable.email..'>', + source = smtp.message(msg), + user = self.user, + password = self.pass, + server = 'smtp.gmail.com', + port = 465, + create = net.sslCreate + } + if not ok then + print("Mail send failed", err) -- better error handling required + end + end + end) +end +function net.sslCreate() + local sock = socket.tcp() + return setmetatable({ + connect = function(_, host, port) + local r, e = sock:connect(host, port) + if not r then return r, e end + sock = ssl.wrap(sock, {mode='client', protocol='tlsv1'}) + return sock:dohandshake() + end + }, { + __index = function(t,n) + return function(_, ...) + return sock[n](sock, ...) + end + end + }) +end + diff --git a/client/net/identity.lua b/client/net/identity.lua new file mode 100644 index 0000000..2cf9d0f --- /dev/null +++ b/client/net/identity.lua @@ -0,0 +1,192 @@ +require("net") +--General Stuff +--[[ What this module does! +Adds +net.identity:init() + +]] +net:registerModule("identity",{1,0,0}) +function net.hash(text,n) + n=n or 16 + return bin.new(text.."jgmhktyf"):getHash(n) +end +function net.identity:init() -- calling this initilizes the library and binds it to the servers and clients created + --Server Stuff + net.OnServerCreated:connect(function(s) + s.userFolder="./" + print("The identity Module has been loaded onto the server!") + function s:_isRegistered(user) + return io.fileExists(self.userFolder..net.hash(user)..".dat") + end + function s:getUserData(user) + local userdata=bin.load(self.userFolder..net.hash(user)..".dat") + local nick,dTable=userdata:match("%S-|%S-|(%S-)|(.+)") + return nick,loadstring("return "..(dTable or "{}"))() + end + function s:getUserCred(user) + local userdata=bin.load(self.userFolder..net.hash(user)..".dat") + return userdata:match("%S-|(%S-)|") + end + function s:userLoggedIn(cid) + for i,v in pairs(self.loggedIn) do + if v.cid==cid then + return i + end + end + return false + end + function s:setDataLocation(loc) + self.userFolder=loc + end + function s:loginUserOut(user) + self.loggedIn[user]=nil + end + function s:loginUserIn(user,cid) + local nick,dTable=self:getUserData(user) + self.loggedIn[user]={} + table.merge(self.loggedIn[user],dTable or {}) + self.loggedIn[user].cid=cid + self.loggedIn[user].nick=nick + return self:getUserDataHandle(user) + end + function s:getUserDataHandle(user) + return self.loggedIn[user] + end + function s:syncUserData(user,ip,port) + local handle=self:getUserDataHandle(user) + self:send(ip,"!identity! SYNC <-|"..bin.ToStr(handle).."|->",port) + end + s.loggedIn={} + s.OnUserRegistered=multi:newConnection() + s.OnUserLoggedIn=multi:newConnection() + s.OnUserLoggerOut=multi:newConnection() + s.OnAlreadyLoggedIn=multi:newConnection() + s.OnPasswordForgotten=multi:newConnection() + s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered + local cmd,arg1,arg2,arg3,arg4 = data:match("!identity! (%S-) '(.-)' '(.-)' '(.-)' <%-|(.+)|%->") + if cmd=="register" then + local user,pass,nick,dTable = arg1,arg2,arg3,arg4 + if self:_isRegistered(user) then + self:send(ip,"!identity! REGISTERED <-|"..user.."|->",port) + else + if not(self.userFolder:sub(-1,-1)=="/" or self.userFolder:sub(-1,-1)=="\\") then + self.userFolder=self.userFolder.."/" + end + local rets=self.OnUserRegistered:Fire(user,pass,nick,loadstring("return "..(dTable or "{}"))()) + for i=1,#rets do + if rets[i][1]==false then + print("Server refused to accept registration request!") + self:send(ip,"!identity! REGISTERREFUSED <-|NIL|->",port) + return + end + end + bin.new(string.format("%s|%s|%s|%s\n",user,pass,nick,dTable)):tofile(self.userFolder..net.hash(user)..".dat") + self:send(ip,"!identity! REGISTEREDGOOD <-|"..user.."|->",port) + end + return + elseif cmd=="login" then + local user,pass = arg1,arg2 + local _pass=s:getUserCred(user) + if not(self:_isRegistered(user)) then + self:send(ip,"!identity! LOGINBAD <-|nil|->",port) + return + end + print(pass,_pass) + if pass==_pass then + if self:userLoggedIn(cid) then + self.OnAlreadyLoggedIn:Fire(self,user,cid,ip,port) + self:send(ip,"!identity! ALREADYLOGGEDIN <-|nil|->",port) + return + end + local handle=self:loginUserIn(user,cid) -- binds the cid to username + self:send(ip,"!identity! LOGINGOOD <-|"..bin.ToStr(handle).."|->",port) + self.OnUserLoggedIn:Fire(user,cid,ip,port) + return + else + self:send(ip,"!identity! LOGINBAD <-|nil|->",port) + return + end + elseif cmd=="logout" then + self:loginUserOut(user) + self.OnClientClosed:Fire(self,"User logged out!",cid,ip,port) + elseif cmd=="sync" then + local dTable = loadstring("return "..(arg4 or "{}"))() + local handle = self:getUserDataHandle(self:userLoggedIn(cid)) + table.merge(handle,dTable) + elseif cmd=="pass" then + local user=arg1 + if self:_isRegistered(user) then + self.OnPasswordForgotten:Fire(arg1,cid) + self:send(ip,"!identity! PASSREQUESTHANDLED <-|NONE|->",port) + else + self:send(ip,"!identity! NOUSER <-|"..user.."|->",port) + end + end + end) + s.OnClientClosed:connect(function(self,reason,cid,ip,port) + self.OnUserLoggerOut:Fire(self,self:userLoggedIn(cid),cid,reason) + end) + end) + --Client Stuff + net.OnClientCreated:connect(function(c) + c.userdata={} + c.OnUserLoggedIn=multi:newConnection() + c.OnBadLogin=multi:newConnection() + c.OnUserAlreadyRegistered=multi:newConnection() + c.OnUserAlreadyLoggedIn=multi:newConnection() + c.OnUserRegistered=multi:newConnection() + c.OnNoUserWithName=multi:newConnection() + c.OnPasswordRequest=multi:newConnection() + c.OnUserRegisterRefused=multi:newConnection() + function c:logout() + self:send("!identity! logout 'NONE' 'NONE' 'NONE' <-|nil|->") + end + c.OnDataRecieved:connect(function(self,data) -- when the client recieves data this method is triggered + local cmd,arg1 = data:match("!identity! (%S-) <%-|(.+)|%->") + if cmd=="REGISTERED" then + self.OnUserAlreadyRegistered:Fire(self,arg1) + elseif cmd=="REGISTEREDGOOD" then + self.OnUserRegistered:Fire(self,arg1) + elseif cmd=="REGISTERREFUSED" then + self.OnUserRegisterRefused:Fire(self,arg1) + elseif cmd=="ALREADYLOGGEDIN" then + self.OnUserAlreadyLoggedIn:Fire(self,arg1) + elseif cmd=="LOGINBAD" then + self.OnBadLogin:Fire(self) + elseif cmd=="LOGINGOOD" then + local dTable=loadstring("return "..(arg1 or "{}"))() + table.merge(self.userdata,dTable) + self.OnUserLoggedIn:Fire(self,self.userdata) + elseif cmd=="SYNC" then + local dTable=loadstring("return "..(arg1 or "{}"))() + table.merge(self.userdata,dTable) + elseif cmd=="NOUSER" then + self.OnNoUserWithName:Fire(self,arg1) + elseif cmd=="PASSREQUESTHANDLED" then + self.OnPasswordRequest:Fire(self) + end + end) + function c:syncUserData() + self:send(string.format("!identity! sync 'NONE' 'NONE' 'NONE' <-|%s|->",bin.ToStr(dTable))) + end + function c:forgotPass(user) + self:send(string.format("!identity! pass '%s' 'NONE' 'NONE' <-|nil|->",user)) + end + function c:getUserDataHandle() + return self.userdata + end + function c:logIn(user,pass) + self:send(string.format("!identity! login '%s' '%s' 'NONE' <-|nil|->",user,net.hash(pass))) + end + function c:register(user,pass,nick,dTable) + if dTable then + self:send(string.format("!identity! register '%s' '%s' '%s' <-|%s|->",user,net.hash(pass),nick,bin.ToStr(dTable))) + else + self:send(string.format("!identity! register '%s' '%s' '%s' <-|nil|->",user,net.hash(pass),nick)) + end + end + end) +end +if net.autoInit then + net.identity:init() +end diff --git a/client/net/init.lua b/client/net/init.lua new file mode 100644 index 0000000..0b67ca1 --- /dev/null +++ b/client/net/init.lua @@ -0,0 +1,483 @@ +function string.trim(s) + local from = s:match"^%s*()" + return from > #s and "" or s:match(".*%S", from) +end +socket=require("socket") +net={} +net.Version={1,0,0} +net.OnServerCreated=multi:newConnection() +net.OnClientCreated=multi:newConnection() +net.loadedModules={} +net.autoInit=true +function net:registerModule(mod,version) + table.insert(self.loadedModules,mod) + net[mod]={} + if version then + net[mod].Version=version + else + net[mod].Version={1,0,0} + end +end +function net.getModuleVersion(ext) + if not ext then + return string.format("%d.%d.%d",net.Version[1],net.Version[2],net.Version[3]) + end + return string.format("%d.%d.%d",net[ext].Version[1],net[ext].Version[2],net[ext].Version[3]) +end +function net.resolveID(obj) + local num=math.random(10000000,99999999) + if obj[tostring(num)] then + return net.resolveID(obj) + end + obj.ids[tostring(num)]=true + return tostring(num) +end +function net.inList(list,dat) + for i,v in pairs(list) do + if v==dat then + return true + end + end + return false +end +function net.setTrigger(funcW,funcE) + multi:newTrigger(func) +end +-- UDP Stuff +function net:newServer(port,servercode) + local c={} + c.udp=assert(socket.udp()) + c.udp:settimeout(0) + c.udp:setsockname("*", port) + c.ips={} + c.Type="udp" + c.port=port + c.ids={} + c.servercode=servercode + c.bannedIPs={} + c.bannedCIDs={} + function c:setUpdateRate(n) + print("Not needed in a udp server!") + end + function c:banCID(cid) + table.insert(self.bannedCIDs,cid) + end + function c:banIP(ip) + table.insert(self.bannedIPs,cid) + end + function c:send(ip,data,port,cid) + if self.servercode then + cid=cid or self:CIDFrom(ip,port) + if not self.ips[cid] then + print("Can't determine cid from client... sending the client a new one!") + local cid=net.resolveID(self) + print("Sending unique cid to client: "..cid) + self.ips[cid]={ip,port,0,self.servercode==nil} + print(ip) + self.udp:sendto("I!"..cid,ip,port) + if self.servercode then + self.udp:sendto("S!",ip,port) + end + return + end + if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then + self.udp:sendto("BANNED CLIENT", ip, port or self.port) + elseif self.ips[cid][4] then + self.udp:sendto(data, ip, port or self.port) + elseif self.ips[cid][4]==false then + self.udp:sendto("Make sure your server code is correct!", ip, port) + end + else + self.udp:sendto(data, ip, port or self.port) + end + end + function c:pollClientModules(ip,port) + self:send(ip,"L!",port) + end + function c:CIDFrom(ip,port) + for i,v in pairs(self.ips) do + if(ip==v[1] and v[2]==port) then + return i + end + end + end + function c:sendAll(data) + for i,v in pairs(self.ips) do + self:send(v[1],data,v[2],i) + end + end + function c:sendAllBut(data,cid) + for i,v in pairs(self.ips) do + if i~=cid then + self:send(v[1],data,v[2],i) + end + end + end + function c:clientRegistered(cid) + return self.ips[cid] + end + function c:clientLoggedIn(cid) + if not self.clientRegistered(cid) then + return nil + end + return self.ips[cid][4] + end + function c:update() + local data,ip,port=self.udp:receivefrom() + if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then + print("We will ingore data from a banned client!") + return + end + if data then + if data:sub(1,4)=="pong" then + print("Recieved pong from: "..data:sub(5,-1)) + self.ips[data:sub(5,-1)][3]=os.clock() + elseif data:sub(1,2)=="S!" then + local cid=self:CIDFrom(ip,port) + if data:sub(3,-1)==self.servercode then + print("Servercode Accepted: "..self.servercode) + if self.ips[cid] then + self.ips[cid][4]=true + else + print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!") + local cid=net.resolveID(self) + print("Sending unique cid to client: "..cid) + self.ips[cid]={ip,port,0,self.servercode==nil} + print(ip) + self.udp:sendto("I!"..cid,ip,port) + if self.servercode then + self.udp:sendto("S!",ip,port) + end + end + else + self.udp:sendto("Make sure your server code is correct!", ip, port) + end + elseif data:sub(1,2)=="C!" then + self.OnDataRecieved:Fire(self,data:sub(11,-1),data:sub(3,10),ip,port) + elseif data:sub(1,2)=="E!" then + self.ips[data:sub(3,10)]=nil + obj.ids[data:sub(3,10)]=false + self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port) + elseif data=="I!" then + local cid=net.resolveID(self) + print("Sending unique cid to client: "..cid) + self.ips[cid]={ip,port,os.clock(),self.servercode==nil} + print(ip) + self.udp:sendto("I!"..cid,ip,port) + if self.servercode then + self.udp:sendto("S!",ip,port) + end + elseif data:sub(1,2)=="L!" then + cid,cList=data:sub(3,10),data:sub(11,-1) + local list={} + for m,v in cList:gmatch("(%S-):(%S-)|") do + list[m]=v + end + self.OnClientsModulesList:Fire(list,cid,ip,port) + end + end + for cid,dat in pairs(self.ips) do + if not((os.clock()-dat[3])<65) then + self.ips[cid]=nil + self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port) + end + end + end + c.OnClientsModulesList=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + c.OnPongRecieved=multi:newConnection() + c.OnClientClosed=multi:newConnection() + c.connectiontest=multi:newAlarm(30) + c.connectiontest.link=c + c.connectiontest:OnRing(function(alarm) + print("pinging clients!") + alarm.link:sendAll("ping") + alarm:Reset() + end) + multi:newLoop(function() + c:update() + end) + net.OnServerCreated:Fire(c) + return c +end + +function net:newClient(host,port,servercode,nonluaServer) + local c={} + c.ip=assert(socket.dns.toip(host)) + c.udp=assert(socket.udp()) + c.udp:setpeername(c.ip, port) + c.udp:settimeout(0) + c.cid="NIL" + c.lastPing=0 + c.Type="udp" + c.servercode=servercode + c.autoReconnect=true + function c:pollPing(n) + return not((os.clock()-self.lastPing)<(n or 60)) + end + function c:send(data) + self.udp:send("C!"..self.cid..data) + end + function c:sendRaw(data) + self.udp:send(data) + end + function c:getCID() + if self:IDAssigned() then + return self.cid + end + end + function c:close() + self:send("E!") + end + function c:IDAssigned() + return self.cid~="NIL" + end + function c:update() + local data=self.udp:receive() + if data then + if data:sub(1,2)=="I!" then + self.cid=data:sub(3,-1) + self.OnClientReady:Fire(self) + elseif data=="S!" then + self.udp:send("S!"..(self.servercode or "")) + elseif data=="L!" then + local mods="" + local m="" + for i=1,#net.loadedModules do + m=net.loadedModules[i] + mods=mods..m..":"..net.getModuleVersion(m).."|" + end + self.udp:send("L!"..self.cid..mods) + elseif data=="ping" then + self.lastPing=os.clock() + self.OnPingRecieved:Fire(self) + self.udp:send("pong"..self.cid) + else + self.OnDataRecieved:Fire(self,data) + end + end + end + function c:reconnect() + if not nonluaServer then + self.cid="NIL" + c.udp:send("I!") + end + end + c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end) + c.pingEvent:OnEvent(function(self) + if self.link.autoReconnect then + self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...") + self.link:reconnect() + else + self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!") + end + end) + c.pingEvent.link=c + c.OnPingRecieved=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + c.OnServerNotAvailable=multi:newConnection() + c.OnClientReady=multi:newConnection() + c.notConnected=multi:newFunction(function(self) + self:hold(3) + if self.link:IDAssigned()==false then + self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server") + end + end) + c.notConnected.link=c + if not nonluaServer then + c.udp:send("I!") + end + multi:newLoop(function() + c:update() + end) + multi:newJob(function() c.notConnected() end) + net.OnClientCreated:Fire(c) + return c +end +--TCP Stuff +function net:newTCPServer(port) + local c={} + c.tcp=assert(socket.bind("*", port)) + c.tcp:settimeout(0) + c.ip,c.port=c.tcp:getsockname() + c.ips={} + c.port=port + c.ids={} + c.bannedIPs={} + c.Type="tcp" + c.rMode="*l" + c.sMode="*l" + c.updaterRate=1 + function c:setUpdateRate(n) + self.updaterRate=n + end + function c:setReceiveMode(mode) + self.rMode=mode + end + function c:setSendMode(mode) + self.rMode=mode + end + function c:banCID(cid) + print("Function not supported on a tcp server!") + end + function c:banIP(ip) + table.insert(self.bannedIPs,cid) + end + function c:send(handle,data) + if self.sMode=="*l" then + handle:send(data.."\n") + else + handle:send(data) + end + end + function c:pollClientModules(ip,port) + self:send(ip,"L!",port) + end + function c:CIDFrom(ip,port) + print("Method not supported when using a TCP Server!") + return "CIDs in TCP work differently!" + end + function c:sendAll(data) + for i,v in pairs(self.ips) do + self:send(v,data) + end + end + function c:sendAllBut(data,cid) + for i,v in pairs(self.ips) do + if not(cid==i) then + self:send(v,data) + end + end + end + function c:clientRegistered(cid) + return self.ips[cid] + end + function c:clientLoggedIn(cid) + return self.ips[cid] + end + function c:update() + local client = self.tcp:accept(self.rMode) + if not client then return end + table.insert(self.ips,client) + client:settimeout(0) + --client:setoption('tcp-nodelay', true) + client:setoption('keepalive', true) + ip,port=client:getpeername() + if ip and port then + print("Got connection from: ",ip,port) + local updater=multi:newUpdater(skip) + updater:OnUpdate(function(self) + local data, err = self.client:receive(self.Link.rMode) + if err=="closed" then + for i=1,#self.Link.ips do + if self.Link.ips[i]==self.client then + table.remove(self.Link.ips,i) + end + end + self.Link.OnClientClosed:Fire(self.Link,"Client Closed Connection!",self.client,self.client,ip) + self:Destroy() + end + if data then + if net.inList(self.Link.bannedIPs,ip) then + print("We will ingore data from a banned client!") + return + end + self.Link.OnDataRecieved:Fire(self.Link,data,self.client,self.client,ip) + if data:sub(1,2)=="L!" then + cList=data + local list={} + for m,v in cList:gmatch("(%S-):(%S-)|") do + list[m]=v + end + self.Link.OnClientsModulesList:Fire(list,self.client,self.client,ip) + end + end + end) + updater:setSkip(self.updaterRate) + updater.client=client + updater.Link=self + end + end + c.OnClientsModulesList=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + c.OnClientClosed=multi:newConnection() + multi:newLoop(function() + c:update() + end) + net.OnServerCreated:Fire(c) + return c +end + +function net:newTCPClient(host,port) + local c={} + c.ip=assert(socket.dns.toip(host)) + c.port=post + c.tcp=socket.connect(c.ip,port) + if not c.tcp then + print("Can't connect to the server: no response from server") + return false + end + c.tcp:settimeout(0) + c.tcp:setoption('tcp-nodelay', true) + c.tcp:setoption('keepalive', true) + c.Type="tcp" + c.autoReconnect=true + c.rMode="*l" + c.sMode="*l" + function c:setReceiveMode(mode) + self.rMode=mode + end + function c:setSendMode(mode) + self.sMode=mode + end + function c:send(data) + if self.sMode=="*l" then + self.tcp:send(data.."\n") + else + self.tcp:send(data) + end + end + function c:sendRaw(data) + self.tcp:send(data) + end + function c:getCID() + return "No Cid on a tcp client!" + end + function c:close() + self.tcp:close() + end + function c:IDAssigned() + return true + end + function c:update() + local data=self.tcp:receive() + if data then + self.OnDataRecieved:Fire(self,data) + end + end + function c:reconnect() + self.ip=assert(socket.dns.toip(host)) + self.tcp=socket.connect(self.ip,self.port) + if not self.tcp then + print("Can't connect to the server: no response from server") + return + end + self.tcp:settimeout(0) + self.tcp:setoption('tcp-nodelay', true) + self.tcp:setoption('keepalive', true) + end + c.event=multi:newEvent(function(event) + return event.link:IDAssigned() + end) + c.event:OnEvent(function(event) + event.link.OnClientReady:Fire(event.link) + end) + c.event.link=c + c.OnClientReady=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + multi:newLoop(function() + c:update() + end) + net.OnClientCreated:Fire(c) + return c +end diff --git a/client/net/sft.lua b/client/net/sft.lua new file mode 100644 index 0000000..6679aa6 --- /dev/null +++ b/client/net/sft.lua @@ -0,0 +1,179 @@ +require("net") +--General Stuff +--[[ What this module does! +Adds + +]] +function io.fileExists(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +net:registerModule("sft",{1,0,0}) +function net.sft:init() -- calling this initilizes the library and binds it to the servers and clients created + --Server Stuff + net.OnServerCreated:connect(function(s) + print("The sft(Simple File Transfer) Module has been loaded onto the server!") + if s.Type~="tcp" then + print("It is recomended that you use tcp to transfer files!") + end + s.transfers={} + s.OnUploadRequest=multi:newConnection() -- create a sft event + s.OnFileUploaded=multi:newConnection() -- create a sft event + s.OnDownloadRequest=multi:newConnection() + s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered + --First Lets make sure we are getting sft data + --filename,dat=data:match("!sft! (%S-) (%S+)") + local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)") + if cmd=="tstart" then + local rets=self.OnUploadRequest:Fire(self,cid,ip,port) + for i=1,#rets do + if rets[i][1]==false then + print("Server refused to accept upload request!") + self:send(ip,"!sft! CANTUPLOAD NIL NIL",port) + return + end + end + local ID,streamable=arg1:match("(.+)|(.+)") + local file,hash=arg2:match("(.+)|(.+)") + if streamable~="NIL" then + self.transfers[ID]={bin.stream(streamable,false),hash,file} + else + self.transfers[ID]={bin.new(""),hash,file} + end + return + elseif cmd=="transfer" then + if self.transfers[arg1]~=nil then + self.transfers[arg1][1]:tackE(bin.fromhex(arg2)) + --print(self.transfers[arg1][1]:getSize()) + end + return + elseif cmd=="tend" then + if self.transfers[arg1]~=nil then + if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then + self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!") + else + print("Hash Error!") + self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!") + end + self.transfers[arg1]=nil + end + return + end + local filename=cmd + local dat=arg1 + if filename==nil then return end + local rets=self.OnDownloadRequest:Fire(self,cid,ip,port) + for i=1,#rets do + if rets[i][1]==false then + print("Server refused to accept download request!") + self:send(ip,"!sft! CANTREQUEST NIL NIL",port) + return + end + end + if io.fileExists(filename) then + --Lets first load the file + local file=bin.stream(filename,false) + local size=file:getSize() + local pieceSize=512 + local pieces=math.ceil(size/pieceSize) + local step=multi:newStep(1,pieces) + step.TransferID=tostring(math.random(1000,9999)) + step.sender=self + step.ip=ip + step.port=port + step.pieceSize=pieceSize + step:OnStart(function(self) + self.sender:send(self.ip,"!sft! TSTART "..self.TransferID.."|"..dat.." "..filename.."|"..file:getHash(32),self.port) + end) + step:OnStep(function(pos,self) + self:hold(.01) + self.sender:send(self.ip,"!sft! TRANSFER "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos)),self.port) + end) + step:OnEnd(function(self) + self.sender:send(self.ip,"!sft! TEND "..self.TransferID.." NIL",self.port) + end) + else + self:send(ip,"!sft! CANTREQUEST NIL NIL",port) + end + end) + end) + --Client Stuff + net.OnClientCreated:connect(function(c) + c.transfers={} + c.OnTransferStarted=multi:newConnection() -- create a sft event + c.OnTransferFinished=multi:newConnection() -- create a sft event + c.OnFileRequestFailed=multi:newConnection() -- create a sft event + c.OnFileUploadFailed=multi:newConnection() -- create a sft event + c.OnDataRecieved:connect(function(self,data) -- when the client recieves data this method is triggered + --First Lets make sure we are getting sft data + local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)") + if cmd=="TSTART" then + local ID,streamable=arg1:match("(.+)|(.+)") + local file,hash=arg2:match("(.+)|(.+)") + if streamable~="NIL" then + self.transfers[ID]={bin.stream(streamable,false),hash,file} + else + self.transfers[ID]={bin.new(""),hash,file} + end + self.OnTransferStarted:Fire(self) + elseif cmd=="TRANSFER" then + self.transfers[arg1][1]:tackE(bin.fromhex(arg2)) + elseif cmd=="TEND" then + if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then + self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!") + else + print("Hash Error!") + self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!") + end + self.transfers[arg1]=nil + elseif cmd=="CANTREQUEST" then + self.OnFileRequestFailed:Fire(self,"Could not request the file for some reason!") + elseif cmd=="CANTUPLOAD" then + self.OnFileUploadFailed:Fire(self,"Could not upload the file for some reason!") + end + end) + function c:uploadFile(filename) + if io.fileExists(filename) then + local file=bin.stream(filename,false) + local size=file:getSize() + local pieceSize=512 + local pieces=math.ceil(size/pieceSize) + local step=multi:newStep(1,pieces) + step.TransferID=tostring(math.random(1000,9999)) + step.sender=self + step.pieceSize=pieceSize + step:OnStart(function(self) + self.sender:send("!sft! tstart "..self.TransferID.."|NIL "..filename.."|"..file:getHash(32)) + end) + step:OnStep(function(pos,self) + self:hold(.01) + self.sender:send("!sft! transfer "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos))) + end) + step:OnEnd(function(self) + print("Request done!") + self.sender:send("!sft! tend "..self.TransferID.." NIL") + end) + else + self.OnFileUploadFailed:Fire(self,filename,"File does not exist!") + end + end + function c:requestFile(filename) + self:send("!sft! "..filename.." NIL NIL NIL") + end + end) +end +if net.autoInit then + net.sft.init() +end diff --git a/client/them.png b/client/them.png new file mode 100644 index 0000000..29c29d8 Binary files /dev/null and b/client/them.png differ diff --git a/client/you.png b/client/you.png new file mode 100644 index 0000000..9e7cbd9 Binary files /dev/null and b/client/you.png differ