diff --git a/lua/entities/gmod_wire_expression2/core/cl_files.lua b/lua/entities/gmod_wire_expression2/core/cl_files.lua index 5af014f9ad..ced640d6a0 100644 --- a/lua/entities/gmod_wire_expression2/core/cl_files.lua +++ b/lua/entities/gmod_wire_expression2/core/cl_files.lua @@ -3,14 +3,14 @@ By: Dan (McLovin) ]]-- -local cv_max_transfer_size = CreateConVar( "wire_expression2_file_max_size", "300", { FCVAR_REPLICATED, FCVAR_ARCHIVE } ) //in kib +local cv_max_transfer_size = CreateConVar("wire_expression2_file_max_size", "300", { FCVAR_REPLICATED, FCVAR_ARCHIVE }, "Maximum file size in kibibytes.") local upload_buffer = {} local download_buffer = {} -local upload_chunk_size = 20000 //Our overhead is pretty small so lets send it in moderate sized pieces, no need to max out the buffer +local upload_chunk_size = 20000 --Our overhead is pretty small so lets send it in moderate sized pieces, no need to max out the buffer -local allowed_directories = { //prefix with >(allowed directory)/file.txt for files outside of e2files/ directory +local allowed_directories = { --prefix with >(allowed directory)/file.txt for files outside of e2files/ directory ["e2files"] = "e2files", ["e2shared"] = "expression2/e2shared", ["cpushared"] = "cpuchip/e2shared", @@ -47,21 +47,22 @@ local function process_filepath( filepath ) return string.GetPathFromFilename( fullpath ) or "e2files/", string.GetFileFromFilename( fullpath ) or "noname.txt" end -/* --- File Read --- */ +--- File Read --- local function upload_callback() if not upload_buffer or not upload_buffer.data then return end - local chunk_size = math.Clamp( string.len( upload_buffer.data ), 0, upload_chunk_size ) + local chunk_size = math.Clamp(#upload_buffer.data, 0, upload_chunk_size) - net.Start("wire_expression2_file_chunk") + net.Start("wire_expression2_file_upload") + net.WriteUInt(2, 2) net.WriteUInt(chunk_size, 32) net.WriteData(upload_buffer.data, chunk_size) net.SendToServer() - upload_buffer.data = string.sub( upload_buffer.data, chunk_size + 1, string.len( upload_buffer.data ) ) + upload_buffer.data = string.sub(upload_buffer.data, chunk_size + 1) if upload_buffer.chunk >= upload_buffer.chunks then - net.Start("wire_expression2_file_finish") net.SendToServer() + net.Start("wire_expression2_file_upload") net.WriteUInt(3, 2) net.SendToServer() timer.Remove( "wire_expression2_file_upload" ) return end @@ -69,66 +70,69 @@ local function upload_callback() upload_buffer.chunk = upload_buffer.chunk + 1 end -net.Receive("wire_expression2_request_file_sp", function(netlen) - local fpath,fname = process_filepath(net.ReadString()) - RunConsoleCommand("wire_expression2_file_singleplayer", fpath .. fname) -end) - net.Receive("wire_expression2_request_file", function(netlen) local fpath,fname = process_filepath(net.ReadString()) - local fullpath = fpath .. fname + if net.ReadBool() then + RunConsoleCommand("wire_expression2_file_singleplayer", fpath .. fname) + else + local fullpath = fpath .. fname - if file.Exists( fullpath,"DATA" ) and file.Size( fullpath, "DATA" ) <= (cv_max_transfer_size:GetInt() * 1024) then - local filedata = file.Read( fullpath,"DATA" ) or "" + if file.Exists( fullpath,"DATA" ) and file.Size( fullpath, "DATA" ) <= (cv_max_transfer_size:GetInt() * 1024) then + local filedata = file.Read( fullpath,"DATA" ) or "" - local len = #filedata + local len = #filedata - upload_buffer = { - chunk = 1, - chunks = math.ceil(len / upload_chunk_size), - data = filedata - } + upload_buffer = { + chunk = 1, + chunks = math.ceil(len / upload_chunk_size), + data = filedata + } - net.Start("wire_expression2_file_begin") - net.WriteUInt(len, 32) - net.SendToServer() + net.Start("wire_expression2_file_upload") + net.WriteUInt(1, 2) + net.WriteUInt(len, 32) + net.SendToServer() - timer.Create( "wire_expression2_file_upload", 1/60, upload_buffer.chunks, upload_callback ) - else - net.Start("wire_expression2_file_begin") - net.WriteUInt(0, 32) // 404 file not found, send len of 0 - net.SendToServer() + timer.Create( "wire_expression2_file_upload", 1/60, upload_buffer.chunks, upload_callback ) + else + net.Start("wire_expression2_file_upload") + net.WriteUInt(1, 2) + net.WriteUInt(0, 32) -- 404 file not found, send len of 0 + net.SendToServer() + end end end ) -/* --- File Write --- */ -net.Receive("wire_expression2_file_download_begin", function( netlen ) - local fpath,fname = process_filepath( net.ReadString() ) - if not E2Lib.isValidFileWritePath(fname) then return end - if not file.Exists(fpath, "DATA") then file.CreateDir(fpath) end - download_buffer = { - name = fpath .. fname, - data = "" - } -end ) - -net.Receive("wire_expression2_file_download_chunk", function( netlen ) - if not download_buffer.name then return end - local len = net.ReadUInt(32) - download_buffer.data = (download_buffer.data or "") .. net.ReadData(len) -end ) - -net.Receive("wire_expresison2_file_download_finish", function( netlen ) - if not download_buffer.name then return end +--- File Write --- +net.Receive("wire_expression2_file_download", function() + local flag = net.ReadUInt(2) + if flag == 2 then -- Chunk + if not download_buffer.name then return end + local len = net.ReadUInt(32) + download_buffer.data = (download_buffer.data or "") .. net.ReadData(len) + + elseif flag == 1 then -- Begin + local fpath,fname = process_filepath( net.ReadString() ) + if not E2Lib.isValidFileWritePath(fname) then return end + if not file.Exists(fpath, "DATA") then file.CreateDir(fpath) end + download_buffer = { + name = fpath .. fname, + data = "" + } + elseif flag == 3 then -- End + if not download_buffer.name then return end - if net.ReadBit() ~= 0 then - file.Append( download_buffer.name, download_buffer.data ) - else - file.Write( download_buffer.name, download_buffer.data ) + if net.ReadBool() then + file.Append( download_buffer.name, download_buffer.data ) + else + file.Write( download_buffer.name, download_buffer.data ) + end + else -- Abort + download_buffer = {} end end ) -/* --- File List --- */ +--- File List --- net.Receive( "wire_expression2_request_list", function( netlen ) local dir = process_filepath(net.ReadString()) @@ -148,3 +152,8 @@ net.Receive( "wire_expression2_request_list", function( netlen ) end net.SendToServer() end ) + +net.Receive("wire_expression2_file_abort", function() + timer.Remove("wire_expression2_file_upload") + print("Aborted") +end) diff --git a/lua/entities/gmod_wire_expression2/core/files.lua b/lua/entities/gmod_wire_expression2/core/files.lua index 6259127e6c..727142042c 100644 --- a/lua/entities/gmod_wire_expression2/core/files.lua +++ b/lua/entities/gmod_wire_expression2/core/files.lua @@ -3,8 +3,8 @@ By: Dan (McLovin) ]]-- -local cv_transfer_delay = CreateConVar( "wire_expression2_file_delay", "5", { FCVAR_ARCHIVE } ) -local cv_max_transfer_size = CreateConVar( "wire_expression2_file_max_size", "300", { FCVAR_REPLICATED, FCVAR_ARCHIVE } ) -- in kib +local cv_max_transfer_size = CreateConVar("wire_expression2_file_max_size", "300", { FCVAR_REPLICATED, FCVAR_ARCHIVE }, "Maximum file size in kibibytes.") +local cv_transfer_max = CreateConVar("wire_expression2_file_max_queue", "5", { FCVAR_ARCHIVE }, "Maximum number of files that can be queued at once.") local download_chunk_size = 20000 -- Our overhead is pretty small so lets send it in moderate sized pieces, no need to max out the buffer @@ -22,99 +22,92 @@ E2Lib.registerConstant( "FILE_TIMEOUT", FILE_TIMEOUT ) E2Lib.registerConstant( "FILE_404", FILE_404 ) E2Lib.registerConstant( "FILE_TRANSFER_ERROR", FILE_TRANSFER_ERROR ) -local delays = WireLib.RegisterPlayerTable() local uploads = WireLib.RegisterPlayerTable() local downloads = WireLib.RegisterPlayerTable() local lists = WireLib.RegisterPlayerTable() local run_on = { file = { - run = 0, name = "", ents = {}, status = FILE_UNKNOWN }, list = { - run = 0, dir = "", ents = {} } } -local function file_canUpload( ply ) - local pfile = uploads[ply] - local pdel = (delays[ply] or {}).upload +local flushFileBuffer - if (pfile and (pfile.uploading or pfile.sp_wait)) or - (pdel and not ply:IsListenServerHost() and (CurTime() - pdel) < cv_transfer_delay:GetInt()) then return false end - - return true +local function file_canUpload(ply) + return #uploads[ply] < cv_transfer_max:GetInt() end -util.AddNetworkString("wire_expression2_request_file_sp") util.AddNetworkString("wire_expression2_request_file") -local function file_Upload( ply, entity, filename ) - if !file_canUpload( ply ) or !IsValid( entity ) or !IsValid( ply ) or !ply:IsPlayer() or !E2Lib.isValidFileWritePath(filename) then return false end +local function file_Upload(self, ply, entity, filename) + if not file_canUpload(ply) then return self:throw("You've reached the file upload limit!") end + if not E2Lib.isValidFileWritePath(filename) then return self:throw("Invalid file name/path: ".. filename) end - uploads[ply] = { + local queue = uploads[ply] + queue[#queue + 1] = { name = filename, - uploading = false, --don't halt other uploads incase file does not exist + uploading = false, uploaded = false, data = "", - ent = entity, - sp_wait = ply:IsListenServerHost() + ent = entity } - net.Start(uploads[ply].sp_wait and "wire_expression2_request_file_sp" or "wire_expression2_request_file") - net.WriteString( filename ) - net.Send(ply) - - delays[ply].upload = CurTime() -end - -local function file_canDownload( ply ) - local pfile = downloads[ply] - local pdel = (delays[ply] or {}).download - - if (pfile and pfile.downloading) or - (pdel and (CurTime() - pdel) < cv_transfer_delay:GetInt()) then return false end + if #queue == 1 then + net.Start("wire_expression2_request_file") + net.WriteString(filename) + net.WriteBool(ply:IsListenServerHost()) + net.Send(ply) + end return true end -local function file_Download( ply, filename, data, append ) - if !file_canDownload( ply ) or !IsValid( ply ) or !ply:IsPlayer() or !E2Lib.isValidFileWritePath(filename) then return false end - if string.len( data ) > (cv_max_transfer_size:GetInt() * 1024) then return false end +local function file_canDownload(ply) + return #downloads[ply] < cv_transfer_max:GetInt() +end + +local function file_Download(self, ply, filename, data, append) + if not file_canDownload(ply) then return self:throw("You've reached the file write limit!") end + if not E2Lib.isValidFileWritePath(filename) then return self:throw("Invalid file name/path: " .. filename) end + if #data > cv_max_transfer_size:GetInt() * 1024 then return self:throw("File is too large to send!") end -- if we're trying to append an empty string then we don't need to queue up -- a download to consider the operation completed - if append and string.len(data) == 0 then return true end + if append and #data == 0 then return 0 end + + local queue = downloads[ply] - downloads[ply] = { + queue[#queue + 1] = { name = filename, data = data, + index = 0, started = false, downloading = true, downloaded = false, - append = append + append = append, + ent = self.entity } -end - -local function file_canList( ply ) - local plist = lists[ply] - local pdel = (delays[ply] or {}).list + flushFileBuffer() - if (plist and plist.uploading) or - (pdel and (CurTime() - pdel) < cv_transfer_delay:GetInt()) then return false end + return #data +end - return true +local function file_canList(ply) + return #lists[ply] < cv_transfer_max:GetInt() end util.AddNetworkString("wire_expression2_request_list") -local function file_List( ply, entity, dir ) - if !file_canList( ply ) or !IsValid( ply ) or !ply:IsPlayer() then return false end +local function file_List(self, ply, entity, dir) + if not file_canList(ply) then return self:throw("You've reached the file list download limit!") end - lists[ply] = { + local queue = lists[ply] + queue[#queue + 1] = { dir = dir, data = {}, uploading = true, @@ -122,126 +115,150 @@ local function file_List( ply, entity, dir ) ent = entity } net.Start("wire_expression2_request_list") - net.WriteString( dir or "" ) + net.WriteString(dir or "") net.Send(ply) - delays[ply].list = CurTime() + return true end --- File loading --- -__e2setcost( 20 ) +__e2setcost(100) -e2function void fileLoad( string filename ) - file_Upload( self.player, self.entity, filename ) +e2function number fileLoad(string filename) + return file_Upload(self, self.player, self.entity, filename) and 1 or 0 end -__e2setcost( 5 ) +__e2setcost(3) +[nodiscard] e2function number fileCanLoad() - return file_canUpload( self.player ) and 1 or 0 + return file_canUpload(self.player) and 1 or 0 end +[nodiscard] +e2function number fileLoadQueued() + return #(uploads[self.player] or {}) +end + +__e2setcost(5) + +[deprecated] e2function number fileLoaded() - local pfile = uploads[self.player] + local pfile = uploads[self.player].last - return (!pfile.uploading and pfile.uploaded) and 1 or 0 + return not pfile.uploading and pfile.uploaded and 1 or 0 end +[deprecated] e2function number fileLoading() - local pfile = uploads[self.player] - - return pfile.uploading and 1 or 0 + return uploads[self.player].last.uploading and 1 or 0 end +[deprecated] e2function number fileStatus() return run_on.file.status or FILE_UNKNOWN end --- File reading/writing --- +[deprecated] e2function string fileName() - local pfile = uploads[self.player] - - if pfile.uploaded and !pfile.uploading then - return pfile.name - end + local pfile = uploads[self.player].last - return "" + return pfile.uploaded and not pfile.uploading and pfile.name or "" end __e2setcost( 10 ) +[deprecated, nodiscard] e2function string fileRead() - local pfile = uploads[self.player] + local pfile = uploads[self.player].last - return (pfile.uploaded and !pfile.uploading) and pfile.data or "" + return pfile.uploaded and not pfile.uploading and pfile.data or "" end -__e2setcost( 5 ) +__e2setcost(3) e2function number fileMaxSize() return cv_max_transfer_size:GetInt() end e2function number fileCanWrite() - return file_canDownload( self.player ) and 1 or 0 + return file_canDownload(self.player) and 1 or 0 +end + +[nodiscard] +e2function number fileWriteQueued() + return #(downloads[self.player] or {}) end -__e2setcost( 20 ) +__e2setcost(100) -e2function void fileWrite( string filename, string data ) - file_Download( self.player, filename, data, false ) +e2function number fileWrite( string filename, string data ) + return file_Download(self, self.player, filename, data, false) or -1 end -e2function void fileAppend( string filename, string data ) - file_Download( self.player, filename, data, true ) +e2function number fileAppend( string filename, string data ) + return file_Download(self, self.player, filename, data, true) or -1 end --- File Listing --- -__e2setcost( 20 ) +__e2setcost(100) -e2function void fileList( string dir ) - file_List( self.player, self.entity, dir ) +e2function number fileList( string dir ) + return file_List(self, self.player, self.entity, dir) and 1 or 0 end -__e2setcost( 5 ) +__e2setcost(3) +[nodiscard] e2function number fileCanList() return file_canList( self.player ) and 1 or 0 end +[nodiscard] +e2function number fileListQueued() + return #(lists[self.player] or {}) +end + +__e2setcost(5) + +[deprecated, nodiscard] e2function number fileLoadedList() - local plist = lists[self.player] + local plist = lists[self.player].last - return (!plist.uploading and plist.uploaded) and 1 or 0 + return not plist.uploading and plist.uploaded and 1 or 0 end +[deprecated, nodiscard] e2function number fileLoadingList() - local plist = lists[self.player] - - return plist.uploading and 1 or 0 + return lists[self.player].last.uploading and 1 or 0 end +[deprecated, nodiscard] e2function array fileReadList() local plist = lists[self.player] - return (plist.uploaded and !plist.uploading and plist.data) and plist.data or {} + return (plist.uploaded and not plist.uploading and plist.data) and plist.data or {} end --- runOnFile event --- __e2setcost( 5 ) +[deprecated] e2function void runOnFile( active ) run_on.file.ents[self.entity] = (active ~= 0) end +[deprecated] e2function number fileClk() return self.data.runOnFile and 1 or 0 end +[deprecated] e2function number fileClk( string filename ) return (self.data.runOnFile and run_on.file.name == filename) and 1 or 0 end @@ -250,213 +267,273 @@ end __e2setcost( 5 ) -e2function void runOnList( active ) - run_on.list.ents[self.entity] = (active ~= 0) +[deprecated] +e2function void runOnList(active) + run_on.list.ents[self.entity] = active ~= 0 end +[deprecated] e2function number fileListClk() return self.data.runOnFileList and 1 or 0 end +[deprecated] e2function number fileListClk( string dir ) return (self.data.runOnFileList and run_on.list.dir == dir) and 1 or 0 end --- Hooks 'n' Shit --- -registerCallback( "construct", function( self ) - uploads[self.player] = uploads[self.player] or { - uploading = false, - uploaded = false - } - downloads[self.player] = downloads[self.player] or { - downloading = false, - downloaded = false - } - lists[self.player] = lists[self.player] or { - uploading = false, - uploaded = false - } - delays[self.player] = delays[self.player] or { - upload = 0, - download = 0, - list = 0 - } +registerCallback("construct", function(self) + local player = self.player + downloads[player] = downloads[player] or { last = {} } + uploads[player] = uploads[player] or { last = {} } + lists[player] = lists[player] or { last = {} } end ) ---- Downloading --- -util.AddNetworkString("wire_expression2_file_download_begin") -util.AddNetworkString("wire_expression2_file_download_chunk") -util.AddNetworkString("wire_expresison2_file_download_finish") -timer.Create("wire_expression2_flush_file_buffer", 0.2, 0, function() - for ply,fdata in pairs( downloads ) do - if IsValid( ply ) and ply:IsPlayer() and fdata.downloading then - if !fdata.started then - net.Start("wire_expression2_file_download_begin") - net.WriteString(fdata.name or "") - net.Send(ply) - - fdata.started = true - end +util.AddNetworkString("wire_expression2_file_abort") - local strlen = math.Clamp( string.len( fdata.data ), 0, download_chunk_size ) +registerCallback("destruct", function(self) + local player, entity = self.player, self.entity - if strlen > 0 then - net.Start("wire_expression2_file_download_chunk") - net.WriteUInt(strlen, 32) - net.WriteData(fdata.data, strlen) - net.Send(ply) + local iterable = { uploads[player], lists[player] } -- Ignore downloads in case the user is backing up data on removed - fdata.data = string.sub( fdata.data, strlen + 1 ) + if iterable[1][1] and iterable[1][1].ent == entity then + net.Start("wire_expression2_file_abort") net.Send(player) -- Special case for uploading files and only uploading files + end + for _, tab in ipairs(iterable) do + local k = 2 + local v = tab[k] + while v do + if v.ent == entity then + table.remove(tab, k) + else + k = k + 1 end + v = tab[k] + end + end +end) - if string.len( fdata.data ) < 1 then - net.Start("wire_expresison2_file_download_finish") - net.WriteBit(fdata.append or false) - net.Send(ply) - - fdata.downloaded = true - fdata.downloading = false +--- Downloading --- +-- Server -> Client +util.AddNetworkString("wire_expression2_file_download") + +-- File transfer flags: +-- 0 - Abort +-- 1 - Begin +-- 2 - Upload +-- 3 - End + +local function flushFileBufferInner() + local die = true + for ply, queue in pairs(downloads) do + if IsValid(ply) then + local fdata = queue[1] + if fdata and fdata.downloading then + if not fdata.started then + net.Start("wire_expression2_file_download") + net.WriteUInt(1, 2) + net.WriteString(fdata.name or "") + net.Send(ply) + + fdata.started = true + die = false + end + + local index = fdata.index + local data = fdata.data + + local strlen = math.min(#data - index, download_chunk_size) + + if strlen > 0 then + net.Start("wire_expression2_file_download") + net.WriteUInt(2, 2) + net.WriteUInt(strlen, 32) + net.WriteData(data:sub(index + 1), strlen) + net.Send(ply) + + fdata.index = strlen + + die = false + elseif strlen <= 0 then + net.Start("wire_expression2_file_download") + net.WriteUInt(3, 2) + net.WriteBool(fdata.append or false) + net.Send(ply) + + fdata.downloaded = true + fdata.downloading = false + + queue.last = fdata + + local ent, name = fdata.ent, fdata.name -- Store as an upvalue in case fdata gets removed + timer.Simple(0, function() -- Trigger one tick ahead so the client has time to write the file + if ent and ent.ExecuteEvent then ent:ExecuteEvent("fileWritten", { name, data }) end + end) + + table.remove(queue, 1) + + if #queue ~= 0 then + die = false + end + end end end end -end) + if die then timer.Remove("wire_expression2_flush_file_buffer") end +end + +flushFileBuffer = function() + if not timer.Exists("wire_expression2_flush_file_buffer") then + timer.Create("wire_expression2_flush_file_buffer", 0.2, 0, flushFileBufferInner) + end +end --- Uploading --- +-- Client -> Server + +local function file_execute(ply, file, status) + + local queue = uploads[ply] + local ent, filename = file.ent + + if ent:IsValid() then + local filename = file.name + run_on.file.name = filename + run_on.file.status = status + + queue.last = file -local function file_execute( ent, filename, status ) - if IsValid(ent) then if status == FILE_OK then - ent:ExecuteEvent("fileLoaded", {filename, uploads[ent.player].data}) + ent:ExecuteEvent("fileLoaded", {filename, file.data}) else ent:ExecuteEvent("fileErrored", {filename, status}) end - elseif !run_on.file.ents[ent] then - return - end - run_on.file.run = 1 - run_on.file.name = filename - run_on.file.status = status + if run_on.file.ents[ent] then + ent.context.data.runOnFile = true + ent:Execute() + ent.context.data.runOnFile = nil + end - ent.context.data.runOnFile = true - ent:Execute() - ent.context.data.runOnFile = nil + run_on.file.name = "" + run_on.file.status = FILE_UNKNOWN + end - run_on.file.run = 0 - run_on.file.name = "" - run_on.file.status = FILE_UNKNOWN + table.remove(queue, 1) + if #queue ~= 0 then + net.Start("wire_expression2_request_file") + net.WriteString(queue[1].name) + net.WriteBool(ply:IsListenServerHost()) + net.Send(ply) + end end -util.AddNetworkString("wire_expression2_file_begin") -net.Receive("wire_expression2_file_begin", function(netlen, ply) - local pfile = uploads[ply] - if !pfile then return end +util.AddNetworkString("wire_expression2_file_upload") +net.Receive("wire_expression2_file_upload", function(_, ply) + local flag = net.ReadUInt(2) + local pfile = uploads[ply][1] + if pfile and pfile.abort then flag = 0 end - local len = net.ReadUInt(32) + if flag == 2 then + if not pfile or not pfile.buffer then return end + if not pfile.uploading then + file_execute(ply, pfile, FILE_TRANSFER_ERROR) + end - if len == 0 then -- file not found - file_execute( pfile.ent, pfile.name, FILE_404 ) - return - end - if (len / 1024) > cv_max_transfer_size:GetInt() then return end + local len = net.ReadUInt(32) + pfile.buffer = pfile.buffer .. net.ReadData(len) - pfile.buffer = "" - pfile.len = len - pfile.uploading = true - pfile.uploaded = false + timer.Create("wire_expression2_file_check_timeout_" .. ply:EntIndex(), 5, 1, function() + local pfile = uploads[ply][1] + if not pfile or pfile.abort then return end + pfile.uploading = false + pfile.uploaded = false + file_execute(ply, pfile, FILE_TIMEOUT) + end) + elseif flag == 1 then + if not pfile then return end - timer.Create( "wire_expression2_file_check_timeout_" .. ply:EntIndex(), 5, 1, function() - local pfile = uploads[ply] - if !pfile then return end - pfile.uploading = false - pfile.uploaded = false - file_execute( pfile.ent, pfile.name, FILE_TIMEOUT ) - end) -end ) + local len = net.ReadUInt(32) -util.AddNetworkString("wire_expression2_file_chunk") -net.Receive("wire_expression2_file_chunk", function(netlen, ply) - local pfile = uploads[ply] - if !pfile or !pfile.buffer then return end - if !pfile.uploading then - file_execute( pfile.ent, pfile.name, FILE_TRANSFER_ERROR ) - end + if len == 0 then -- file not found + file_execute(ply, pfile, FILE_404) + return + end + if (len / 1024) > cv_max_transfer_size:GetInt() then return end - local len = net.ReadUInt(32) - pfile.buffer = pfile.buffer .. net.ReadData(len) + pfile.buffer = "" + pfile.len = len + pfile.uploading = true + pfile.uploaded = false - local timername = "wire_expression2_file_check_timeout_" .. ply:EntIndex() - if timer.Exists( timername ) then - timer.Create( timername, 5, 1, function() - local pfile = uploads[ply] - if !pfile then return end + timer.Create("wire_expression2_file_check_timeout_" .. ply:EntIndex(), 5, 1, function() + local pfile = uploads[ply][1] + if not pfile or pfile.abort then return end pfile.uploading = false pfile.uploaded = false - file_execute( pfile.ent, pfile.name, FILE_TIMEOUT ) + file_execute(ply, pfile, FILE_TIMEOUT) end) - end -end ) + elseif flag == 3 then + timer.Remove("wire_expression2_file_check_timeout_" .. ply:EntIndex()) -util.AddNetworkString("wire_expression2_file_finish") -net.Receive("wire_expression2_file_finish", function(netlen, ply) - local timername = "wire_expression2_file_check_timeout_" .. ply:EntIndex() + if not pfile or not pfile.buffer then return end - if timer.Exists( timername ) then - timer.Remove( timername ) - end + pfile.uploading = false + pfile.data = pfile.buffer + pfile.buffer = "" - local pfile = uploads[ply] - if !pfile or !pfile.buffer then return end + if string.len( pfile.data ) ~= pfile.len then -- transfer error + pfile.data = "" + file_execute(ply, pfile, FILE_TRANSFER_ERROR) + return + end + pfile.uploaded = true - pfile.uploading = false - pfile.data = pfile.buffer - pfile.buffer = "" + file_execute(ply, pfile, FILE_OK) + else + timer.Remove("wire_expression2_file_check_timeout_" .. ply:EntIndex()) - if string.len( pfile.data ) ~= pfile.len then -- transfer error - pfile.data = "" - file_execute( pfile.ent, pfile.name, FILE_TRANSFER_ERROR ) - return + if #uploads[ply] ~= 0 then + net.Start("wire_expression2_request_file") + net.WriteString(queue[1].name) + net.WriteBool(ply:IsListenServerHost()) + net.Send(ply) + end end - pfile.uploaded = true - - file_execute( pfile.ent, pfile.name, FILE_OK ) -end ) +end) concommand.Add("wire_expression2_file_singleplayer", function(ply, cmd, args) if not ply:IsListenServerHost() then ply:Kick("Do not use wire_expression2_file_singleplayer in multiplayer, unless you're the host!") end - local pfile = uploads[ply] - if !pfile then return end + local pfile = uploads[ply][1] + if not pfile then return end local path = args[1] if not file.Exists(path, "DATA") then - pfile.sp_wait = false - file_execute( pfile.ent, pfile.name, FILE_404 ) + file_execute(ply, pfile, FILE_404) return end - local timername = "wire_expression2_file_check_timeout_" .. ply:EntIndex() - - if timer.Exists(timername) then timer.Remove(timername) end + timer.Remove("wire_expression2_file_check_timeout_" .. ply:EntIndex()) pfile.uploading = false pfile.data = file.Read(path) pfile.buffer = "" pfile.uploaded = true - pfile.sp_wait = false - file_execute(pfile.ent, pfile.name, FILE_OK) + file_execute(ply, pfile, FILE_OK) end) --- Listing --- util.AddNetworkString("wire_expression2_file_list") net.Receive("wire_expression2_file_list", function(netlen, ply) - local plist = lists[ply] - if !plist then return end + local queue = lists[ply] + local plist = queue[1] + if not plist then return end - local timername = "wire_expression2_filelist_check_timeout_" .. ply:EntIndex() - if timer.Exists( timername ) then timer.Remove( timername ) end + timer.Remove("wire_expression2_filelist_check_timeout_" .. ply:EntIndex()) for i=1, net.ReadUInt(16) do table.insert(plist.data, net.ReadData(net.ReadUInt(16))) @@ -465,15 +542,28 @@ net.Receive("wire_expression2_file_list", function(netlen, ply) plist.uploaded = true plist.uploading = false - run_on.list.run = 1 - run_on.list.dir = plist.dir + local ent = plist.ent + if ent:IsValid() then + + run_on.list.dir = plist.dir + + ent:ExecuteEvent("fileList", { plist.dir, plist.data }) + + if run_on.list.ents[plist.ent] then + ent.context.data.runOnFileList = true + ent:Execute() + ent.context.data.runOnFileList = nil + end - plist.ent.context.data.runOnFileList = true - plist.ent:Execute() - plist.ent.context.data.runOnFileList = nil + run_on.list.dir = "" + end - run_on.list.run = 0 - run_on.list.dir = "" + table.remove(queue, 1) + if #queue ~= 0 then + net.Start("wire_expression2_request_list") + net.WriteString(queue[1].dir) + net.Send(ply) + end end ) E2Lib.registerEvent("fileErrored", { @@ -483,4 +573,12 @@ E2Lib.registerEvent("fileErrored", { E2Lib.registerEvent("fileLoaded", { { "FilePath", "s" }, { "Data", "s" } +}) +E2Lib.registerEvent("fileWritten", { + { "FilePath", "s" }, + { "Data", "s" } +}) +E2Lib.registerEvent("fileList", { + { "Path", "s"}, + { "Contents", "r" } }) \ No newline at end of file diff --git a/lua/wire/client/e2descriptions.lua b/lua/wire/client/e2descriptions.lua index fd3f3eaee1..420b7daf7a 100644 --- a/lua/wire/client/e2descriptions.lua +++ b/lua/wire/client/e2descriptions.lua @@ -1250,22 +1250,26 @@ E2Helper.Descriptions["fileClk(s)"] = "Returns whether the execution was run bec E2Helper.Descriptions["fileClk()"] = "Returns whether the execution was run because a file finished uploading" E2Helper.Descriptions["fileLoadedList()"] = "If the list has been loaded and it is called, it will return 1. Any time after that until a new list is loaded it will return 0" E2Helper.Descriptions["fileLoadingList()"] = "Returns whether a list is currently uploading" -E2Helper.Descriptions["fileList(s)"] = "Returns an array of file names that have been loaded" +E2Helper.Descriptions["fileList(s)"] = "Loads a list of file names in the directory" E2Helper.Descriptions["fileListTable()"] = "Returns a table of file names that have been loaded. (Tbl[\"filename\"] = \"filename\")" E2Helper.Descriptions["fileListClk(s)"] = "Returns whether the execution was run because a list with specified name was uploaded to the server" E2Helper.Descriptions["fileListClk()"] = "Returns whether the execution was run because a list was uploaded to the server" +E2Helper.Descriptions["fileListQueued()"] = "Returns the number of lists in the list queue" E2Helper.Descriptions["fileAppend(ss)"] = "Adds string data to the end of the file" E2Helper.Descriptions["fileWrite(ss)"] = "Writes string data to the file overwriting it" +E2Helper.Descriptions["fileWriteQueued()"] = "Returns the number of files in the write queue" E2Helper.Descriptions["fileCanList()"] = "Returns 1 if the file list can be uploaded to the server" E2Helper.Descriptions["fileCanLoad()"] = "Returns 1 if the file can be loaded" E2Helper.Descriptions["fileCanWrite()"] = "Returns 1 if the file can be written" E2Helper.Descriptions["fileLoad(s)"] = "Loads specified file to the server" +E2Helper.Descriptions["fileLoadQueued()"] = "Returns the number of files in the load queue" E2Helper.Descriptions["fileLoading()"] = "Returns whether a file is currently uploading" E2Helper.Descriptions["fileMaxSize()"] = "Returns the maximum file size that can be uploaded or downloaded. Default is 300 KiB" E2Helper.Descriptions["fileName()"] = "Returns the name of the last uploaded file, or an empty string if there is no currently uploaded file" E2Helper.Descriptions["fileStatus()"] = "Returns the status of the upload in progress. Returns one of _FILE_UNKNOWN, _FILE_OK, _FILE_TIMEOUT, _FILE_404 or _FILE_TRANSFER_ERROR" E2Helper.Descriptions["runOnFile(n)"] = "Specifies whether the E2 will run when a file finishes uploading" -E2Helper.Descriptions["runOnList(n)"] = "Specifies whether the E2 will run when a list finishes uploading" +E2Helper.Descriptions["runOnFileList(n)"] = "Specifies whether the E2 will run when a file list finishes uploading" +E2Helper.Descriptions["runOnList(n)"] = "Specifies whether the E2 will run when a file list finishes uploading" -- Datasignals E2Helper.Descriptions["dsSend"] = "Sends a datasignal to the specified group and scope"