Semi-working Lua scripts
This commit is contained in:
committed by
Maplewheels
parent
295c365a8f
commit
3d51abc56b
@@ -85,7 +85,8 @@ namespace Barotrauma
|
||||
// menus and navigation states
|
||||
case MainMenuScreen:
|
||||
case ModDownloadScreen:
|
||||
case ServerListScreen:
|
||||
case ServerListScreen:
|
||||
SetRunState(RunState.Unloaded);
|
||||
SetRunState(RunState.LoadedNoExec);
|
||||
break;
|
||||
// running lobby or editor states
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
local compatibilityLib = {}
|
||||
|
||||
local networking = LuaUserData.RegisterType("Barotrauma.LuaCsNetworking")
|
||||
-- local networking = LuaUserData.RegisterType("Barotrauma.LuaCsNetworking")
|
||||
|
||||
LuaUserData.AddMethod(networking, "RequestGetHTTP", Networking.HttpGet)
|
||||
-- LuaUserData.AddMethod(networking, "RequestGetHTTP", Networking.HttpGet)
|
||||
|
||||
LuaUserData.AddMethod(networking, "RequestPostHTTP", Networking.HttpPost)
|
||||
-- LuaUserData.AddMethod(networking, "RequestPostHTTP", Networking.HttpPost)
|
||||
|
||||
compatibilityLib.CreateVector2 = Vector2.__new
|
||||
compatibilityLib.CreateVector3 = Vector3.__new
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
if true then return end
|
||||
|
||||
Hook.Patch("Barotrauma.Item", "TryInteract",
|
||||
{
|
||||
"Barotrauma.Character",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
LuaSetup = {}
|
||||
|
||||
local path = table.pack(...)[1]
|
||||
local path, resourcesToExecute = ...
|
||||
|
||||
package.path = {path .. "/?.lua"}
|
||||
package.path = {path .. "/Lua/?.lua"}
|
||||
|
||||
setmodulepaths(package.path)
|
||||
|
||||
@@ -44,4 +44,8 @@ require("PostSetup")
|
||||
|
||||
LuaSetup = nil
|
||||
|
||||
require("ModLoader")
|
||||
for resource in resourcesToExecute do
|
||||
for path in resource.FilePaths do
|
||||
dofile(path)
|
||||
end
|
||||
end
|
||||
@@ -1,193 +0,0 @@
|
||||
local LUA_MOD_REQUIRE_PATH = "/Lua/?.lua"
|
||||
local LUA_MOD_AUTORUN_PATH = "/Lua/Autorun"
|
||||
local LUA_MOD_FORCEDAUTORUN_PATH = "/Lua/ForcedAutorun"
|
||||
|
||||
local function EndsWith(str, suffix)
|
||||
return str:sub(-string.len(suffix)) == suffix
|
||||
end
|
||||
|
||||
local function GetFileName(file)
|
||||
return file:match("^.+/(.+)$")
|
||||
end
|
||||
|
||||
local function ExecuteProtected(s, folder)
|
||||
loadfile(s)(folder)
|
||||
end
|
||||
|
||||
local function RunFolder(folder, rootFolder, package)
|
||||
local search = File.DirSearch(folder)
|
||||
for i = 1, #search, 1 do
|
||||
local s = search[i]:gsub("\\", "/")
|
||||
|
||||
if EndsWith(s, ".lua") then
|
||||
local time = os.clock()
|
||||
local ok, result = pcall(ExecuteProtected, s, rootFolder)
|
||||
local diff = os.clock() - time
|
||||
|
||||
print(string.format(" - %s (Took %.5fms)", GetFileName(s), diff))
|
||||
if not ok then
|
||||
printerror(result)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local function AssertTypes(expectedTypes, ...)
|
||||
local args = table.pack(...)
|
||||
assert(
|
||||
#args == #expectedTypes,
|
||||
string.format(
|
||||
"Assertion failed: incorrect number of args\n\texpected = %s\n\tgot = %s",
|
||||
#expectedTypes, #args
|
||||
)
|
||||
)
|
||||
for i = 1, #args do
|
||||
local arg = args[i]
|
||||
local expectedType = expectedTypes[i]
|
||||
assert(
|
||||
type(arg) == expectedType,
|
||||
string.format(
|
||||
"Assertion failed: incorrect argument type (arg #%d)\n\texpected = %s\n\tgot = %s",
|
||||
i, expectedType, type(arg)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local function ExecutionQueue()
|
||||
local executionQueue = {}
|
||||
executionQueue.Queue = {}
|
||||
|
||||
executionQueue.Process = function()
|
||||
while executionQueue.Queue[1] ~= nil do
|
||||
local folder, rootFolder, package = table.unpack(table.remove(executionQueue.Queue, 1))
|
||||
print(string.format("%s %s", package.Name, package.ModVersion))
|
||||
RunFolder(folder, rootFolder, package)
|
||||
end
|
||||
end
|
||||
|
||||
executionQueue.Add = function(...)
|
||||
AssertTypes({ 'string', 'string', 'userdata' }, ...)
|
||||
table.insert(executionQueue.Queue, table.pack(...))
|
||||
end
|
||||
|
||||
return executionQueue
|
||||
end
|
||||
|
||||
local QueueAutorun = ExecutionQueue()
|
||||
local QueueForcedAutorun = ExecutionQueue()
|
||||
|
||||
local function nocase(s)
|
||||
s = string.gsub(s, "%a", function(c)
|
||||
return string.format("[%s%s]", string.lower(c), string.upper(c))
|
||||
end)
|
||||
return s
|
||||
end
|
||||
|
||||
local function ProcessPackages(packages, fn)
|
||||
for pkg in packages do
|
||||
if pkg then
|
||||
local pkgPath = pkg.Path
|
||||
:gsub("\\", "/")
|
||||
:gsub(nocase("/filelist.xml"), "")
|
||||
fn(pkg, pkgPath)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ProcessPackages(ContentPackageManager.EnabledPackages.All, function(pkg, pkgPath)
|
||||
table.insert(package.path, pkgPath .. LUA_MOD_REQUIRE_PATH)
|
||||
local autorunPath = pkgPath .. LUA_MOD_AUTORUN_PATH
|
||||
if File.DirectoryExists(autorunPath) then
|
||||
QueueAutorun.Add(autorunPath, pkgPath, pkg)
|
||||
end
|
||||
end)
|
||||
|
||||
-- we don't want to execute workshop ForcedAutorun if we have a local Package
|
||||
local executedLocalPackages = {}
|
||||
|
||||
ProcessPackages(ContentPackageManager.EnabledPackages.All, function(pkg, pkgPath)
|
||||
table.insert(package.path, pkgPath .. LUA_MOD_REQUIRE_PATH)
|
||||
local forcedAutorunPath = pkgPath .. LUA_MOD_FORCEDAUTORUN_PATH
|
||||
if File.DirectoryExists(forcedAutorunPath) then
|
||||
QueueForcedAutorun.Add(forcedAutorunPath, pkgPath, pkg)
|
||||
executedLocalPackages[pkg.Name] = true
|
||||
end
|
||||
end)
|
||||
|
||||
if not LuaCsConfig.TreatForcedModsAsNormal then
|
||||
ProcessPackages(ContentPackageManager.LocalPackages, function(pkg, pkgPath)
|
||||
if not executedLocalPackages[pkg.Name] then
|
||||
table.insert(package.path, pkgPath .. LUA_MOD_REQUIRE_PATH)
|
||||
local forcedAutorunPath = pkgPath .. LUA_MOD_FORCEDAUTORUN_PATH
|
||||
if File.DirectoryExists(forcedAutorunPath) then
|
||||
QueueForcedAutorun.Add(forcedAutorunPath, pkgPath, pkg)
|
||||
executedLocalPackages[pkg.Name] = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ProcessPackages(ContentPackageManager.AllPackages, function(pkg, pkgPath)
|
||||
if not executedLocalPackages[pkg.Name] then
|
||||
table.insert(package.path, pkgPath .. LUA_MOD_REQUIRE_PATH)
|
||||
local forcedAutorunPath = pkgPath .. LUA_MOD_FORCEDAUTORUN_PATH
|
||||
if File.DirectoryExists(forcedAutorunPath) then
|
||||
QueueForcedAutorun.Add(forcedAutorunPath, pkgPath, pkg)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
setmodulepaths(package.path)
|
||||
setmodulepaths = nil
|
||||
|
||||
local allExecuted = {}
|
||||
for key, value in pairs(QueueAutorun.Queue) do table.insert(allExecuted, value[3]) end
|
||||
for key, value in pairs(QueueForcedAutorun.Queue) do table.insert(allExecuted, value[3]) end
|
||||
|
||||
if SERVER then
|
||||
Networking.Receive("_luastart", function (message, client)
|
||||
local num = message.ReadUInt16()
|
||||
|
||||
local packages = {}
|
||||
|
||||
for i = 1, num, 1 do
|
||||
table.insert(packages, {
|
||||
Name = message.ReadString(),
|
||||
Version = message.ReadString(),
|
||||
Id = message.ReadUInt64(),
|
||||
Hash = message.ReadString()
|
||||
})
|
||||
end
|
||||
|
||||
Hook.Call("client.packages", client, packages)
|
||||
end)
|
||||
elseif Game.IsMultiplayer then
|
||||
local message = Networking.Start("_luastart")
|
||||
|
||||
message.WriteUInt16(#allExecuted)
|
||||
|
||||
for key, package in pairs(allExecuted) do
|
||||
local id = package.UgcId
|
||||
local hash = package.Hash and package.Hash.StringRepresentation or ""
|
||||
|
||||
if id == nil then id = 0 end
|
||||
|
||||
message.WriteString(package.Name)
|
||||
message.WriteString(package.ModVersion)
|
||||
message.WriteUInt64(UInt64(id))
|
||||
message.WriteString(hash)
|
||||
end
|
||||
|
||||
Networking.Send(message)
|
||||
end
|
||||
|
||||
QueueAutorun.Process()
|
||||
QueueForcedAutorun.Process()
|
||||
|
||||
Hook.Add("stop", "luaSetup.stop", function()
|
||||
print("Stopping Lua...")
|
||||
end)
|
||||
|
||||
Hook.Call("loaded")
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
internal class LuaUserData
|
||||
{
|
||||
[Obsolete("Use IPluginManagementService::GetTypesByName()")]
|
||||
public static Type GetType(string typeName) => throw new NotImplementedException(); //LuaCsSetup.GetType(typeName);
|
||||
public static Type GetType(string typeName) => GameMain.LuaCs.PluginManagementService.GetType(typeName); //LuaCsSetup.GetType(typeName);
|
||||
|
||||
public static IUserDataDescriptor RegisterType(string typeName)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Barotrauma
|
||||
SubscribeToLuaCsEvents();
|
||||
}
|
||||
|
||||
bool ValidateLuaCsContent()
|
||||
private bool ValidateLuaCsContent()
|
||||
{
|
||||
#if DEBUG
|
||||
// TODO: we just wanna boot for now
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void SubscribeToLuaCsEvents()
|
||||
private void SubscribeToLuaCsEvents()
|
||||
{
|
||||
EventService.Subscribe<IEventScreenSelected>(this); // game state hook in
|
||||
EventService.Subscribe<IEventEnabledPackageListChanged>(this);
|
||||
@@ -283,7 +283,13 @@ namespace Barotrauma
|
||||
DisposeLuaCsConfig();
|
||||
Logger.LogResults(PackageManagementService.UnloadAllPackages());
|
||||
}
|
||||
|
||||
|
||||
LuaScriptManagementService.Reset();
|
||||
PackageManagementService.Reset();
|
||||
EventService.Reset();
|
||||
|
||||
SubscribeToLuaCsEvents();
|
||||
|
||||
CurrentRunState = RunState.Unloaded;
|
||||
}
|
||||
|
||||
|
||||
@@ -348,6 +348,7 @@ public class EventService : IEventService, IEventAssemblyContextUnloading
|
||||
_subscriptions.Clear();
|
||||
_luaSubscriptionFactories.Clear();
|
||||
_eventTypeNameAliases.Clear();
|
||||
_luaLegacySubscriptionFactories.Clear();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
@@ -357,6 +358,7 @@ public class EventService : IEventService, IEventAssemblyContextUnloading
|
||||
_subscriptions.Clear();
|
||||
_luaSubscriptionFactories.Clear();
|
||||
_eventTypeNameAliases.Clear();
|
||||
_luaLegacySubscriptionFactories.Clear();
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using FluentResults;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MoonSharp.Interpreter;
|
||||
|
||||
@@ -132,6 +133,42 @@ public partial class LoggerService : ILoggerService
|
||||
#endif
|
||||
}
|
||||
|
||||
public void HandleException(Exception ex)
|
||||
{
|
||||
string errorString = "";
|
||||
switch (ex)
|
||||
{
|
||||
case NetRuntimeException netRuntimeException:
|
||||
if (netRuntimeException.DecoratedMessage == null)
|
||||
{
|
||||
errorString = netRuntimeException.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: netRuntimeException.ToString() doesn't print the InnerException's stack trace...
|
||||
errorString = $"{netRuntimeException.DecoratedMessage}: {netRuntimeException}";
|
||||
}
|
||||
break;
|
||||
case InterpreterException interpreterException:
|
||||
if (interpreterException.DecoratedMessage == null)
|
||||
{
|
||||
errorString = interpreterException.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorString = interpreterException.DecoratedMessage;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errorString = ex.StackTrace != null
|
||||
? ex.ToString()
|
||||
: $"{ex}\n{Environment.StackTrace}";
|
||||
break;
|
||||
}
|
||||
|
||||
LogError(errorString);
|
||||
}
|
||||
|
||||
public void LogResults(FluentResults.Result result)
|
||||
{
|
||||
if (result == null)
|
||||
@@ -149,7 +186,15 @@ public partial class LoggerService : ILoggerService
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
LogError(error.Message);
|
||||
if (error is ExceptionalError exceptionalError)
|
||||
{
|
||||
HandleException(exceptionalError.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError(error.Message);
|
||||
}
|
||||
|
||||
|
||||
if (error.Reasons != null)
|
||||
{
|
||||
|
||||
@@ -7,10 +7,12 @@ using Barotrauma.Networking;
|
||||
using FluentResults;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using MonoMod.RuntimeDetour;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using MoonSharp.Interpreter.Loaders;
|
||||
using RestSharp.Validation;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -18,12 +20,12 @@ using System.Collections.Immutable;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using static Barotrauma.GameSettings;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
@@ -97,6 +99,46 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
return new FluentResults.Result().WithReasons(cacheRes.Value.SelectMany(cr => cr.Item2.Reasons));
|
||||
}
|
||||
|
||||
private DynValue DoFile(string file, Table? globalContext = null, string? codeStringFriendly = null)
|
||||
{
|
||||
if (_script == null)
|
||||
{
|
||||
throw new Exception("Not running");
|
||||
}
|
||||
|
||||
if (!LuaCsFile.CanReadFromPath(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"dofile: File access to {file} not allowed.");
|
||||
}
|
||||
|
||||
if (!LuaCsFile.Exists(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"dofile: File {file} not found.");
|
||||
}
|
||||
|
||||
return _script.DoFile(file, globalContext, codeStringFriendly);
|
||||
}
|
||||
|
||||
private DynValue LoadFile(string file, Table? globalContext = null, string? codeStringFriendly = null)
|
||||
{
|
||||
if (_script == null)
|
||||
{
|
||||
throw new Exception("Not running");
|
||||
}
|
||||
|
||||
if (!LuaCsFile.CanReadFromPath(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"loadfile: File access to {file} not allowed.");
|
||||
}
|
||||
|
||||
if (!LuaCsFile.Exists(file))
|
||||
{
|
||||
throw new ScriptRuntimeException($"loadfile: File {file} not found.");
|
||||
}
|
||||
|
||||
return _script.LoadFile(file, globalContext, codeStringFriendly);
|
||||
}
|
||||
|
||||
private void SetupEnvironment()
|
||||
{
|
||||
_script = new Script(CoreModules.Preset_SoftSandbox | CoreModules.Debug | CoreModules.IO | CoreModules.OS_System);
|
||||
@@ -115,9 +157,21 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
RegisterType(typeof(ILuaCsUtility));
|
||||
RegisterType(typeof(ILuaCsTimer));
|
||||
RegisterType(typeof(LuaCsFile));
|
||||
RegisterType(typeof(ILuaScriptResourceInfo));
|
||||
RegisterType(typeof(IResourceInfo));
|
||||
RegisterType(typeof(LuaUserData));
|
||||
RegisterType(typeof(IUserDataDescriptor));
|
||||
|
||||
new LuaConverters(_script).RegisterLuaConverters();
|
||||
|
||||
var luaRequire = new LuaRequire(_script);
|
||||
|
||||
_script.Globals["setmodulepaths"] = (string[] str) => ((LuaScriptLoader)_luaScriptLoader).ModulePaths = str;
|
||||
|
||||
_script.Globals["dofile"] = (Func<string, Table, string, DynValue>)DoFile;
|
||||
_script.Globals["loadfile"] = (Func<string, Table, string, DynValue>)LoadFile;
|
||||
_script.Globals["require"] = (Func<string, Table, DynValue>)luaRequire.Require;
|
||||
|
||||
_script.Globals["printerror"] = (DynValue o) => { LuaCsLogger.LogError(o.ToString()); };
|
||||
|
||||
_script.Globals["dostring"] = (Func<string, Table, string, DynValue>)_script.DoString;
|
||||
@@ -129,6 +183,7 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
_script.Globals["File"] = UserData.CreateStatic<LuaCsFile>();
|
||||
//_script.Globals["Networking"] = _luaCsNetworking;
|
||||
//_script.Globals["Steam"] = Steam;
|
||||
_script.Globals["LuaUserData"] = UserData.CreateStatic<LuaUserData>();
|
||||
|
||||
_script.Globals["ExecutionNumber"] = 0;
|
||||
_script.Globals["CSActive"] = false;
|
||||
@@ -138,9 +193,7 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
}
|
||||
|
||||
public FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaScriptResourceInfo> executionOrder)
|
||||
{
|
||||
throw new NotImplementedException($"Need to implement {nameof(executionOrder)} logic.");
|
||||
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
return FluentResults.Result.Fail("Tried to execute Lua scripts without unloading first.");
|
||||
@@ -152,13 +205,16 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
|
||||
var result = FluentResults.Result.Ok();
|
||||
|
||||
foreach (ILuaScriptResourceInfo resource in _resourcesInfo)
|
||||
List<ILuaScriptResourceInfo> initializationScripts = executionOrder.Where(x => x.OwnerPackage.Name == "LuaCsForBarotrauma").ToList();
|
||||
List<ILuaScriptResourceInfo> otherScripts = executionOrder.Except(initializationScripts).ToList();
|
||||
|
||||
foreach (ILuaScriptResourceInfo resource in initializationScripts)
|
||||
{
|
||||
foreach (ContentPath filePath in resource.FilePaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
_script?.Call(_script.LoadFile(filePath.FullPath));
|
||||
_script?.Call(_script.LoadFile(filePath.FullPath), Path.GetDirectoryName(resource.OwnerPackage.Path), otherScripts.ToList());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ public class PluginManagementService : IPluginManagementService, IAssemblyManage
|
||||
if (IsDisposed)
|
||||
return FluentResults.Result.Fail($"{nameof(PluginManagementService)} is disposed!");
|
||||
|
||||
throw new NotImplementedException();
|
||||
return FluentResults.Result.Fail("not implemented");
|
||||
}
|
||||
|
||||
public Result<ImmutableArray<Type>> GetImplementingTypes<T>(bool includeInterfaces = false,
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
using System;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics.Common;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
@@ -6,12 +13,6 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using FarseerPhysics.Common;
|
||||
using FluentResults;
|
||||
using FluentResults.LuaCs;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services.Safe;
|
||||
@@ -40,6 +41,17 @@ public class SafeStorageService : StorageService, ISafeStorageService
|
||||
try
|
||||
{
|
||||
path = GetFullPath(path);
|
||||
|
||||
if (path.StartsWith(ConfigData.WorkshopModsDirectory)
|
||||
|| path.StartsWith(ConfigData.LocalModsDirectory)
|
||||
#if CLIENT
|
||||
|| path.StartsWith(ConfigData.TempDownloadsDirectory)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_fileListRead.ContainsKey(path))
|
||||
{
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user