Replace sandbox wrapper scripts.
This commit is contained in:
committed by
abcdefg30
parent
e8dd85419f
commit
445b736885
2
Makefile
2
Makefile
@@ -130,7 +130,7 @@ endif
|
||||
check-scripts:
|
||||
@echo
|
||||
@echo "Checking for Lua syntax errors..."
|
||||
@find lua/ mods/*/{maps,scripts}/ -iname "*.lua" -print0 | xargs -0n1 luac -p
|
||||
@find mods/*/maps/ mods/*/scripts/ -iname "*.lua" -print0 | xargs -0n1 luac -p
|
||||
|
||||
test: all
|
||||
@echo
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
@@ -143,6 +142,8 @@ namespace OpenRA.Scripting
|
||||
public readonly Cache<ActorInfo, Type[]> ActorCommands;
|
||||
public readonly Type[] PlayerCommands;
|
||||
|
||||
public string ErrorMessage;
|
||||
|
||||
bool disposed;
|
||||
|
||||
public ScriptContext(World world, WorldRenderer worldRenderer,
|
||||
@@ -165,64 +166,110 @@ namespace OpenRA.Scripting
|
||||
.ToArray();
|
||||
PlayerCommands = FilterCommands(world.Map.Rules.Actors[SystemActors.Player], knownPlayerCommands);
|
||||
|
||||
runtime.Globals["EngineDir"] = Platform.EngineDir;
|
||||
runtime.DoBuffer(File.Open(Path.Combine(Platform.EngineDir, "lua", "scriptwrapper.lua"), FileMode.Open, FileAccess.Read).ReadAllText(), "scriptwrapper.lua").Dispose();
|
||||
tick = (LuaFunction)runtime.Globals["Tick"];
|
||||
// Safe functions for http://lua-users.org/wiki/SandBoxes
|
||||
// assert, error have been removed as well as albeit safe
|
||||
var allowedGlobals = new string[]
|
||||
{
|
||||
"ipairs", "next", "pairs",
|
||||
"pcall", "select", "tonumber", "tostring", "type", "unpack", "xpcall",
|
||||
"math", "string", "table"
|
||||
};
|
||||
|
||||
foreach (var fieldName in runtime.Globals.Keys)
|
||||
{
|
||||
if (!allowedGlobals.Contains(fieldName.ToString()))
|
||||
runtime.Globals[fieldName] = null;
|
||||
}
|
||||
|
||||
var forbiddenMath = new string[]
|
||||
{
|
||||
"random", // not desync safe, unsuitable
|
||||
"randomseed" // maybe unsafe as it affects the host RNG
|
||||
};
|
||||
|
||||
var mathGlobal = (LuaTable)runtime.Globals["math"];
|
||||
foreach (var mathFunction in mathGlobal.Keys)
|
||||
{
|
||||
if (forbiddenMath.Contains(mathFunction.ToString()))
|
||||
mathGlobal[mathFunction] = null;
|
||||
}
|
||||
|
||||
// Register globals
|
||||
runtime.Globals["EngineDir"] = Platform.EngineDir;
|
||||
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)FatalError))
|
||||
runtime.Globals["FatalError"] = fn;
|
||||
|
||||
runtime.Globals["MaxUserScriptInstructions"] = MaxUserScriptInstructions;
|
||||
|
||||
using (var registerGlobal = (LuaFunction)runtime.Globals["RegisterSandboxedGlobal"])
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)LogDebugMessage))
|
||||
runtime.Globals["print"] = fn;
|
||||
|
||||
// Register global tables
|
||||
var bindings = Game.ModData.ObjectCreator.GetTypesImplementing<ScriptGlobal>();
|
||||
foreach (var b in bindings)
|
||||
{
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)LogDebugMessage))
|
||||
registerGlobal.Call("print", fn).Dispose();
|
||||
|
||||
// Register global tables
|
||||
var bindings = Game.ModData.ObjectCreator.GetTypesImplementing<ScriptGlobal>();
|
||||
foreach (var b in bindings)
|
||||
var ctor = b.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
|
||||
{
|
||||
var ctor = b.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
|
||||
{
|
||||
var p = c.GetParameters();
|
||||
return p.Length == 1 && p.First().ParameterType == typeof(ScriptContext);
|
||||
});
|
||||
var p = c.GetParameters();
|
||||
return p.Length == 1 && p.First().ParameterType == typeof(ScriptContext);
|
||||
});
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException($"{b.Name} must define a constructor that takes a ScriptContext context parameter");
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException($"{b.Name} must define a constructor that takes a {nameof(ScriptContext)} context parameter");
|
||||
|
||||
var binding = (ScriptGlobal)ctor.Invoke(new[] { this });
|
||||
using (var obj = binding.ToLuaValue(this))
|
||||
registerGlobal.Call(binding.Name, obj).Dispose();
|
||||
}
|
||||
var binding = (ScriptGlobal)ctor.Invoke(new[] { this });
|
||||
using (var obj = binding.ToLuaValue(this))
|
||||
runtime.Globals.Add(binding.Name, obj);
|
||||
}
|
||||
|
||||
// System functions do not count towards the memory limit
|
||||
runtime.MaxMemoryUse = runtime.MemoryUse + MaxUserScriptMemory;
|
||||
|
||||
using (var loadScript = (LuaFunction)runtime.Globals["ExecuteSandboxedScript"])
|
||||
try
|
||||
{
|
||||
foreach (var s in scripts)
|
||||
loadScript.Call(s, world.Map.Open(s).ReadAllText()).Dispose();
|
||||
foreach (var script in scripts)
|
||||
runtime.DoBuffer(world.Map.Open(script).ReadAllText(), script).Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FatalError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
tick = (LuaFunction)runtime.Globals["Tick"];
|
||||
}
|
||||
|
||||
void LogDebugMessage(string message)
|
||||
{
|
||||
Console.WriteLine("Lua debug: {0}", message);
|
||||
Console.WriteLine($"Lua debug: {message}");
|
||||
Log.Write("lua", message);
|
||||
}
|
||||
|
||||
public bool FatalErrorOccurred { get; private set; }
|
||||
public void FatalError(string message)
|
||||
public void FatalError(Exception e)
|
||||
{
|
||||
ErrorMessage = e.Message;
|
||||
|
||||
Console.WriteLine($"Fatal Lua Error: {e.Message}");
|
||||
Console.WriteLine(e.StackTrace);
|
||||
|
||||
Log.Write("lua", $"Fatal Lua Error: {e.Message}");
|
||||
Log.Write("lua", e.StackTrace);
|
||||
|
||||
FatalErrorOccurred = true;
|
||||
|
||||
World.AddFrameEndTask(w => World.EndGame());
|
||||
}
|
||||
|
||||
void FatalError(string message)
|
||||
{
|
||||
var stacktrace = new StackTrace().ToString();
|
||||
|
||||
Console.WriteLine($"Fatal Lua Error: {message}");
|
||||
Console.WriteLine(stacktrace);
|
||||
|
||||
Log.Write("lua", $"Fatal Lua Error: {message}");
|
||||
Log.Write("lua", message);
|
||||
Log.Write("lua", stacktrace);
|
||||
|
||||
FatalErrorOccurred = true;
|
||||
@@ -232,14 +279,11 @@ namespace OpenRA.Scripting
|
||||
|
||||
public void RegisterMapActor(string name, Actor a)
|
||||
{
|
||||
using (var registerGlobal = (LuaFunction)runtime.Globals["RegisterSandboxedGlobal"])
|
||||
{
|
||||
if (runtime.Globals.ContainsKey(name))
|
||||
throw new LuaException($"The global name '{name}' is reserved, and may not be used by a map actor");
|
||||
if (runtime.Globals.ContainsKey(name))
|
||||
throw new LuaException($"The global name '{name}' is reserved, and may not be used by a map actor");
|
||||
|
||||
using (var obj = a.ToLuaValue(this))
|
||||
registerGlobal.Call(name, obj).Dispose();
|
||||
}
|
||||
using (var obj = a.ToLuaValue(this))
|
||||
runtime.Globals.Add(name, obj);
|
||||
}
|
||||
|
||||
public void WorldLoaded()
|
||||
@@ -247,8 +291,15 @@ namespace OpenRA.Scripting
|
||||
if (FatalErrorOccurred)
|
||||
return;
|
||||
|
||||
using (var worldLoaded = (LuaFunction)runtime.Globals["WorldLoaded"])
|
||||
worldLoaded.Call().Dispose();
|
||||
try
|
||||
{
|
||||
using (var worldLoaded = (LuaFunction)runtime.Globals["WorldLoaded"])
|
||||
worldLoaded.Call().Dispose();
|
||||
}
|
||||
catch (LuaException e)
|
||||
{
|
||||
FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
@@ -256,8 +307,15 @@ namespace OpenRA.Scripting
|
||||
if (FatalErrorOccurred || disposed)
|
||||
return;
|
||||
|
||||
using (new PerfSample("tick_lua"))
|
||||
tick.Call().Dispose();
|
||||
try
|
||||
{
|
||||
using (new PerfSample("tick_lua"))
|
||||
tick.Call().Dispose();
|
||||
}
|
||||
catch (LuaException e)
|
||||
{
|
||||
FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.FatalError(ex.Message);
|
||||
context.FatalError(ex);
|
||||
}
|
||||
|
||||
Dispose();
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (LuaException e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.FatalError(e.Message);
|
||||
Context.FatalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
public class LuaScript : ITick, IWorldLoaded, INotifyActorDisposing
|
||||
{
|
||||
readonly LuaScriptInfo info;
|
||||
ScriptContext context;
|
||||
public ScriptContext Context;
|
||||
bool disposed;
|
||||
|
||||
public LuaScript(LuaScriptInfo info)
|
||||
@@ -40,13 +40,13 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
void IWorldLoaded.WorldLoaded(World world, WorldRenderer worldRenderer)
|
||||
{
|
||||
var scripts = info.Scripts ?? Enumerable.Empty<string>();
|
||||
context = new ScriptContext(world, worldRenderer, scripts);
|
||||
context.WorldLoaded();
|
||||
Context = new ScriptContext(world, worldRenderer, scripts);
|
||||
Context.WorldLoaded();
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
context.Tick();
|
||||
Context.Tick();
|
||||
}
|
||||
|
||||
void INotifyActorDisposing.Disposing(Actor self)
|
||||
@@ -54,11 +54,11 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
context?.Dispose();
|
||||
Context?.Dispose();
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public bool FatalErrorOccurred => context.FatalErrorOccurred;
|
||||
public bool FatalErrorOccurred => Context.FatalErrorOccurred;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -274,7 +274,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -296,7 +296,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -338,7 +338,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -361,7 +361,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -385,7 +385,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -408,7 +408,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -431,7 +431,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -446,7 +446,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -467,7 +467,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -488,7 +488,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -507,7 +507,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
f.Context.FatalError(ex.Message);
|
||||
f.Context.FatalError(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
37
OpenRA.Mods.Common/Widgets/Logic/Ingame/ScriptErrorLogic.cs
Normal file
37
OpenRA.Mods.Common/Widgets/Logic/Ingame/ScriptErrorLogic.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Mods.Common.Scripting;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
class ScriptErrorLogic : ChromeLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public ScriptErrorLogic(Widget widget, World world)
|
||||
{
|
||||
var panel = widget.Get<ScrollPanelWidget>("SCRIPT_ERROR_MESSAGE_PANEL");
|
||||
var label = widget.Get<LabelWidget>("SCRIPT_ERROR_MESSAGE");
|
||||
var font = Game.Renderer.Fonts[label.Font];
|
||||
|
||||
var luaScript = world.WorldActor.TraitOrDefault<LuaScript>();
|
||||
if (luaScript != null)
|
||||
{
|
||||
var text = WidgetUtils.WrapText(luaScript.Context.ErrorMessage, label.Bounds.Width, font);
|
||||
label.Text = text;
|
||||
label.Bounds.Height = font.Measure(text).Y;
|
||||
panel.ScrollToTop();
|
||||
panel.Layout.AdjustChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
lua/sandbox.lua
163
lua/sandbox.lua
@@ -1,163 +0,0 @@
|
||||
local sandbox = {
|
||||
_VERSION = "sandbox 0.5",
|
||||
_DESCRIPTION = "A pure-lua solution for running untrusted Lua code.",
|
||||
_URL = "https://github.com/kikito/sandbox.lua",
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2013 Enrique García Cota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
}
|
||||
|
||||
-- The base environment is merged with the given env option (or an empty table, if no env provided)
|
||||
--
|
||||
local BASE_ENV = {}
|
||||
|
||||
-- List of non-safe packages/functions:
|
||||
--
|
||||
-- * string.rep: can be used to allocate millions of bytes in 1 operation
|
||||
-- * {set|get}metatable: can be used to modify the metatable of global objects (strings, integers)
|
||||
-- * collectgarbage: can affect performance of other systems
|
||||
-- * dofile: can access the server filesystem
|
||||
-- * _G: It has access to everything. It can be mocked to other things though.
|
||||
-- * load{file|string}: All unsafe because they can grant acces to global env
|
||||
-- * raw{get|set|equal}: Potentially unsafe
|
||||
-- * module|require|module: Can modify the host settings
|
||||
-- * string.dump: Can display confidential server info (implementation of functions)
|
||||
-- * string.rep: Can allocate millions of bytes in one go
|
||||
-- * math.randomseed: Can affect the host sytem
|
||||
-- * io.*, os.*: Most stuff there is non-save
|
||||
|
||||
|
||||
-- Safe packages/functions below
|
||||
([[
|
||||
|
||||
_VERSION assert error ipairs next pairs
|
||||
pcall select tonumber tostring type unpack xpcall
|
||||
|
||||
coroutine.create coroutine.resume coroutine.running coroutine.status
|
||||
coroutine.wrap coroutine.yield
|
||||
|
||||
math.abs math.acos math.asin math.atan math.atan2 math.ceil
|
||||
math.cos math.cosh math.deg math.exp math.fmod math.floor
|
||||
math.frexp math.huge math.ldexp math.log math.log10 math.max
|
||||
math.min math.modf math.pi math.pow math.rad
|
||||
math.sin math.sinh math.sqrt math.tan math.tanh
|
||||
|
||||
os.clock os.difftime os.time
|
||||
|
||||
string.byte string.char string.find string.format string.gmatch
|
||||
string.gsub string.len string.lower string.match string.reverse
|
||||
string.sub string.upper
|
||||
|
||||
table.insert table.maxn table.remove table.sort
|
||||
|
||||
]]):gsub('%S+', function(id)
|
||||
local module, method = id:match('([^%.]+)%.([^%.]+)')
|
||||
if module then
|
||||
BASE_ENV[module] = BASE_ENV[module] or {}
|
||||
BASE_ENV[module][method] = _G[module][method]
|
||||
else
|
||||
BASE_ENV[id] = _G[id]
|
||||
end
|
||||
end)
|
||||
|
||||
local function protect_module(module, module_name)
|
||||
return setmetatable({}, {
|
||||
__index = module,
|
||||
__newindex = function(_, attr_name, _)
|
||||
error('Can not modify ' .. module_name .. '.' .. attr_name .. '. Protected by the sandbox.')
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
('coroutine math os string table'):gsub('%S+', function(module_name)
|
||||
BASE_ENV[module_name] = protect_module(BASE_ENV[module_name], module_name)
|
||||
end)
|
||||
|
||||
-- auxiliary functions/variables
|
||||
|
||||
local string_rep = string.rep
|
||||
|
||||
local function merge(dest, source)
|
||||
for k,v in pairs(source) do
|
||||
dest[k] = dest[k] or v
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
local function sethook(f, key, quota)
|
||||
if type(debug) ~= 'table' or type(debug.sethook) ~= 'function' then return end
|
||||
debug.sethook(f, key, quota)
|
||||
end
|
||||
|
||||
local function cleanup()
|
||||
sethook()
|
||||
string.rep = string_rep
|
||||
end
|
||||
|
||||
-- Public interface: sandbox.protect
|
||||
function sandbox.protect(f, options)
|
||||
if type(f) == 'string' then f = assert(loadstring(f)) end
|
||||
|
||||
options = options or {}
|
||||
|
||||
local quota = false
|
||||
if options.quota ~= false then
|
||||
quota = options.quota or 500000
|
||||
end
|
||||
|
||||
local env = merge(options.env or {}, BASE_ENV)
|
||||
env._G = env._G or env
|
||||
|
||||
setfenv(f, env)
|
||||
|
||||
return function(...)
|
||||
|
||||
if quota then
|
||||
local timeout = function()
|
||||
cleanup()
|
||||
error('Quota exceeded: ' .. tostring(quota))
|
||||
end
|
||||
sethook(timeout, "", quota)
|
||||
end
|
||||
|
||||
string.rep = nil
|
||||
|
||||
local ok, result = pcall(f, ...)
|
||||
|
||||
cleanup()
|
||||
|
||||
if not ok then error(result) end
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
-- Public interface: sandbox.run
|
||||
function sandbox.run(f, options, ...)
|
||||
return sandbox.protect(f, options)(...)
|
||||
end
|
||||
|
||||
-- make sandbox(f) == sandbox.protect(f)
|
||||
setmetatable(sandbox, {__call = function(_,f,o) return sandbox.protect(f,o) end})
|
||||
|
||||
return sandbox
|
||||
@@ -1,52 +0,0 @@
|
||||
--[[
|
||||
Copyright (c) The OpenRA Developers and Contributors
|
||||
This file is part of OpenRA, which is free software. It is made
|
||||
available to you under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version. For more
|
||||
information, see COPYING.
|
||||
]]
|
||||
environment = {}
|
||||
|
||||
-- Reset package path
|
||||
package.path = EngineDir .. "/lua/?.lua"
|
||||
|
||||
-- Note: sandbox has been customized to remove math.random
|
||||
local sandbox = require('sandbox')
|
||||
local stp = require('stacktraceplus')
|
||||
|
||||
local PrintStackTrace = function(msg)
|
||||
return stp.stacktrace("", 2) .. "\nError message\n===============\n" .. msg .. "\n==============="
|
||||
end
|
||||
|
||||
local TryRunSandboxed = function(fn)
|
||||
local success, err = xpcall(function() sandbox.run(fn, {env = environment, quota = MaxUserScriptInstructions}) end, PrintStackTrace)
|
||||
if not success then
|
||||
FatalError(err)
|
||||
end
|
||||
end
|
||||
|
||||
WorldLoaded = function()
|
||||
if environment.WorldLoaded ~= nil then
|
||||
TryRunSandboxed(environment.WorldLoaded)
|
||||
end
|
||||
end
|
||||
|
||||
Tick = function()
|
||||
if environment.Tick ~= nil then
|
||||
TryRunSandboxed(environment.Tick)
|
||||
end
|
||||
end
|
||||
|
||||
ExecuteSandboxedScript = function(file, contents)
|
||||
local script, err = loadstring(contents, file)
|
||||
if (script == nil) then
|
||||
FatalError("Error parsing " .. file .. ". Reason: " .. err)
|
||||
else
|
||||
TryRunSandboxed(script)
|
||||
end
|
||||
end
|
||||
|
||||
RegisterSandboxedGlobal = function(key, value)
|
||||
environment[key] = value
|
||||
end
|
||||
@@ -1,436 +0,0 @@
|
||||
--[[
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010 Ignacio Burgue<75>o
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Source: https://github.com/ignacio/StackTracePlus
|
||||
]]
|
||||
-- tables
|
||||
local _G = _G
|
||||
local string, io, debug, coroutine = string, io, debug, coroutine
|
||||
|
||||
-- functions
|
||||
local tostring, print, require = tostring, print, require
|
||||
local next, assert = next, assert
|
||||
local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs
|
||||
local error = error
|
||||
|
||||
assert(debug, "debug table must be available at this point")
|
||||
|
||||
local io_open = io.open
|
||||
local string_gmatch = string.gmatch
|
||||
local string_sub = string.sub
|
||||
local table_concat = table.concat
|
||||
|
||||
local _M = {
|
||||
max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)'
|
||||
}
|
||||
|
||||
-- this tables should be weak so the elements in them won't become uncollectable
|
||||
local m_known_tables = { [_G] = "_G (global table)" }
|
||||
local function add_known_module(name, desc)
|
||||
local ok, mod = pcall(require, name)
|
||||
if ok then
|
||||
m_known_tables[mod] = desc
|
||||
end
|
||||
end
|
||||
|
||||
add_known_module("string", "string module")
|
||||
add_known_module("io", "io module")
|
||||
add_known_module("os", "os module")
|
||||
add_known_module("table", "table module")
|
||||
add_known_module("math", "math module")
|
||||
add_known_module("package", "package module")
|
||||
add_known_module("debug", "debug module")
|
||||
add_known_module("coroutine", "coroutine module")
|
||||
|
||||
-- lua5.2
|
||||
add_known_module("bit32", "bit32 module")
|
||||
-- luajit
|
||||
add_known_module("bit", "bit module")
|
||||
add_known_module("jit", "jit module")
|
||||
|
||||
|
||||
local m_user_known_tables = {}
|
||||
|
||||
local m_known_functions = {}
|
||||
for _, name in ipairs{
|
||||
-- Lua 5.2, 5.1
|
||||
"assert",
|
||||
"collectgarbage",
|
||||
"dofile",
|
||||
"error",
|
||||
"getmetatable",
|
||||
"ipairs",
|
||||
"load",
|
||||
"loadfile",
|
||||
"next",
|
||||
"pairs",
|
||||
"pcall",
|
||||
"print",
|
||||
"rawequal",
|
||||
"rawget",
|
||||
"rawlen",
|
||||
"rawset",
|
||||
"require",
|
||||
"select",
|
||||
"setmetatable",
|
||||
"tonumber",
|
||||
"tostring",
|
||||
"type",
|
||||
"xpcall",
|
||||
|
||||
-- Lua 5.1
|
||||
"gcinfo",
|
||||
"getfenv",
|
||||
"loadstring",
|
||||
"module",
|
||||
"newproxy",
|
||||
"setfenv",
|
||||
"unpack",
|
||||
-- TODO: add table.* etc functions
|
||||
} do
|
||||
if _G[name] then
|
||||
m_known_functions[_G[name]] = name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local m_user_known_functions = {}
|
||||
|
||||
local function safe_tostring (value)
|
||||
local ok, err = pcall(tostring, value)
|
||||
if ok then return err else return ("<failed to get printable value>: '%s'"):format(err) end
|
||||
end
|
||||
|
||||
-- Private:
|
||||
-- Parses a line, looking for possible function definitions (in a very na<6E>ve way)
|
||||
-- Returns '(anonymous)' if no function name was found in the line
|
||||
local function ParseLine(line)
|
||||
assert(type(line) == "string")
|
||||
--print(line)
|
||||
local match = line:match("^%s*function%s+(%w+)")
|
||||
if match then
|
||||
--print("+++++++++++++function", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("^%s*local%s+function%s+(%w+)")
|
||||
if match then
|
||||
--print("++++++++++++local", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("^%s*local%s+(%w+)%s+=%s+function")
|
||||
if match then
|
||||
--print("++++++++++++local func", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("%s*function%s*%(") -- this is an anonymous function
|
||||
if match then
|
||||
--print("+++++++++++++function2", match)
|
||||
return "(anonymous)"
|
||||
end
|
||||
return "(anonymous)"
|
||||
end
|
||||
|
||||
-- Private:
|
||||
-- Tries to guess a function's name when the debug info structure does not have it.
|
||||
-- It parses either the file or the string where the function is defined.
|
||||
-- Returns '?' if the line where the function is defined is not found
|
||||
local function GuessFunctionName(info)
|
||||
--print("guessing function name")
|
||||
if type(info.source) == "string" and info.source:sub(1,1) == "@" then
|
||||
local file, err = io_open(info.source:sub(2), "r")
|
||||
if not file then
|
||||
print("file not found: "..tostring(err)) -- whoops!
|
||||
return "?"
|
||||
end
|
||||
local line
|
||||
for i = 1, info.linedefined do
|
||||
line = file:read("*l")
|
||||
end
|
||||
if not line then
|
||||
print("line not found") -- whoops!
|
||||
return "?"
|
||||
end
|
||||
return ParseLine(line)
|
||||
else
|
||||
local line
|
||||
local lineNumber = 0
|
||||
for l in string_gmatch(info.source, "([^\n]+)\n-") do
|
||||
lineNumber = lineNumber + 1
|
||||
if lineNumber == info.linedefined then
|
||||
line = l
|
||||
break
|
||||
end
|
||||
end
|
||||
if not line then
|
||||
print("line not found") -- whoops!
|
||||
return "?"
|
||||
end
|
||||
return ParseLine(line)
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Dumper instances are used to analyze stacks and collect its information.
|
||||
--
|
||||
local Dumper = {}
|
||||
|
||||
Dumper.new = function(thread)
|
||||
local t = { lines = {} }
|
||||
for k,v in pairs(Dumper) do t[k] = v end
|
||||
|
||||
t.dumping_same_thread = (thread == coroutine.running())
|
||||
|
||||
-- if a thread was supplied, bind it to debug.info and debug.get
|
||||
-- we also need to skip this additional level we are introducing in the callstack (only if we are running
|
||||
-- in the same thread we're inspecting)
|
||||
if type(thread) == "thread" then
|
||||
t.getinfo = function(level, what)
|
||||
if t.dumping_same_thread and type(level) == "number" then
|
||||
level = level + 1
|
||||
end
|
||||
return debug.getinfo(thread, level, what)
|
||||
end
|
||||
t.getlocal = function(level, loc)
|
||||
if t.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
return debug.getlocal(thread, level, loc)
|
||||
end
|
||||
else
|
||||
t.getinfo = debug.getinfo
|
||||
t.getlocal = debug.getlocal
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- helpers for collecting strings to be used when assembling the final trace
|
||||
function Dumper:add (text)
|
||||
self.lines[#self.lines + 1] = text
|
||||
end
|
||||
function Dumper:add_f (fmt, ...)
|
||||
self:add(fmt:format(...))
|
||||
end
|
||||
function Dumper:concat_lines ()
|
||||
return table_concat(self.lines)
|
||||
end
|
||||
|
||||
---
|
||||
-- Private:
|
||||
-- Iterates over the local variables of a given function.
|
||||
--
|
||||
-- @param level The stack level where the function is.
|
||||
--
|
||||
function Dumper:DumpLocals (level)
|
||||
local prefix = "\t "
|
||||
local i = 1
|
||||
|
||||
if self.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
|
||||
local name, value = self.getlocal(level, i)
|
||||
if not name then
|
||||
return
|
||||
end
|
||||
self:add("\tLocal variables:\r\n")
|
||||
while name do
|
||||
if type(value) == "number" then
|
||||
self:add_f("%s%s = number: %g\r\n", prefix, name, value)
|
||||
elseif type(value) == "boolean" then
|
||||
self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value))
|
||||
elseif type(value) == "string" then
|
||||
self:add_f("%s%s = string: %q\r\n", prefix, name, value)
|
||||
elseif type(value) == "userdata" then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value))
|
||||
elseif type(value) == "nil" then
|
||||
self:add_f("%s%s = nil\r\n", prefix, name)
|
||||
elseif type(value) == "table" then
|
||||
if m_known_tables[value] then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value])
|
||||
elseif m_user_known_tables[value] then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value])
|
||||
else
|
||||
local txt = "{"
|
||||
for k,v in pairs(value) do
|
||||
txt = txt..safe_tostring(k)..":"..safe_tostring(v)
|
||||
if #txt > _M.max_tb_output_len then
|
||||
txt = txt.." (more...)"
|
||||
break
|
||||
end
|
||||
if next(value, k) then txt = txt..", " end
|
||||
end
|
||||
self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}")
|
||||
end
|
||||
elseif type(value) == "function" then
|
||||
local info = self.getinfo(value, "nS")
|
||||
local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value]
|
||||
if info.what == "C" then
|
||||
self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value)))
|
||||
else
|
||||
local source = info.short_src
|
||||
if source:sub(2,7) == "string" then
|
||||
source = source:sub(9)
|
||||
end
|
||||
--for k,v in pairs(info) do print(k,v) end
|
||||
fun_name = fun_name or GuessFunctionName(info)
|
||||
self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source)
|
||||
end
|
||||
elseif type(value) == "thread" then
|
||||
self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value))
|
||||
end
|
||||
i = i + 1
|
||||
name, value = self.getlocal(level, i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Public:
|
||||
-- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc.
|
||||
-- This function is suitable to be used as an error handler with pcall or xpcall
|
||||
--
|
||||
-- @param thread An optional thread whose stack is to be inspected (defaul is the current thread)
|
||||
-- @param message An optional error string or object.
|
||||
-- @param level An optional number telling at which level to start the traceback (default is 1)
|
||||
--
|
||||
-- Returns a string with the stack trace and a string with the original error.
|
||||
--
|
||||
function _M.stacktrace(thread, message, level)
|
||||
if type(thread) ~= "thread" then
|
||||
-- shift parameters left
|
||||
thread, message, level = nil, thread, message
|
||||
end
|
||||
|
||||
thread = thread or coroutine.running()
|
||||
|
||||
level = level or 1
|
||||
|
||||
local dumper = Dumper.new(thread)
|
||||
|
||||
local original_error
|
||||
|
||||
if type(message) == "table" then
|
||||
dumper:add("an error object {\r\n")
|
||||
local first = true
|
||||
for k,v in pairs(message) do
|
||||
if first then
|
||||
dumper:add(" ")
|
||||
first = false
|
||||
else
|
||||
dumper:add(",\r\n ")
|
||||
end
|
||||
dumper:add(safe_tostring(k))
|
||||
dumper:add(": ")
|
||||
dumper:add(safe_tostring(v))
|
||||
end
|
||||
dumper:add("\r\n}")
|
||||
original_error = dumper:concat_lines()
|
||||
elseif type(message) == "string" then
|
||||
dumper:add(message)
|
||||
original_error = message
|
||||
end
|
||||
|
||||
dumper:add("\r\n")
|
||||
dumper:add[[
|
||||
Stack Traceback
|
||||
===============
|
||||
]]
|
||||
--print(error_message)
|
||||
|
||||
local level_to_show = level
|
||||
if dumper.dumping_same_thread then level = level + 1 end
|
||||
|
||||
local info = dumper.getinfo(level, "nSlf")
|
||||
while info do
|
||||
if info.what == "main" then
|
||||
if string_sub(info.source, 1, 1) == "@" then
|
||||
dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, string_sub(info.source, 2), info.currentline)
|
||||
else
|
||||
dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.short_src, info.currentline)
|
||||
end
|
||||
elseif info.what == "C" then
|
||||
--print(info.namewhat, info.name)
|
||||
--for k,v in pairs(info) do print(k,v, type(v)) end
|
||||
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func)
|
||||
dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name)
|
||||
--dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value)))
|
||||
elseif info.what == "tail" then
|
||||
--print("tail")
|
||||
--for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name)
|
||||
dumper:add_f("(%d) tail call\r\n", level_to_show)
|
||||
dumper:DumpLocals(level)
|
||||
elseif info.what == "Lua" then
|
||||
local source = info.short_src
|
||||
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name
|
||||
if source:sub(2, 7) == "string" then
|
||||
source = source:sub(9)
|
||||
end
|
||||
local was_guessed = false
|
||||
if not function_name or function_name == "?" then
|
||||
--for k,v in pairs(info) do print(k,v, type(v)) end
|
||||
function_name = GuessFunctionName(info)
|
||||
was_guessed = true
|
||||
end
|
||||
-- test if we have a file name
|
||||
local function_type = (info.namewhat == "") and "function" or info.namewhat
|
||||
if info.source and info.source:sub(1, 1) == "@" then
|
||||
dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
elseif info.source and info.source:sub(1,1) == '#' then
|
||||
dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
else
|
||||
dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, function_name, info.currentline, source)
|
||||
end
|
||||
dumper:DumpLocals(level)
|
||||
else
|
||||
dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what)
|
||||
end
|
||||
|
||||
level = level + 1
|
||||
level_to_show = level_to_show + 1
|
||||
info = dumper.getinfo(level, "nSlf")
|
||||
end
|
||||
|
||||
return dumper:concat_lines(), original_error
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds a table to the list of known tables
|
||||
function _M.add_known_table(tab, description)
|
||||
if m_known_tables[tab] then
|
||||
error("Cannot override an already known table")
|
||||
end
|
||||
m_user_known_tables[tab] = description
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds a function to the list of known functions
|
||||
function _M.add_known_function(fun, description)
|
||||
if m_known_functions[fun] then
|
||||
error("Cannot override an already known function")
|
||||
end
|
||||
m_user_known_functions[fun] = description
|
||||
end
|
||||
|
||||
return _M
|
||||
4
make.ps1
4
make.ps1
@@ -141,10 +141,6 @@ function Check-Scripts-Command
|
||||
{
|
||||
luac -p $script
|
||||
}
|
||||
foreach ($script in ls "lua/*.lua")
|
||||
{
|
||||
luac -p $script
|
||||
}
|
||||
foreach ($script in ls "mods/*/scripts/*.lua")
|
||||
{
|
||||
luac -p $script
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
Container@SCRIPT_ERROR_PANEL:
|
||||
Height: PARENT_BOTTOM
|
||||
Width: PARENT_RIGHT
|
||||
Children:
|
||||
Label@DESCA:
|
||||
X: 15
|
||||
Y: 15
|
||||
Width: PARENT_RIGHT - 30
|
||||
Height: 20
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Text: The map script has encountered a fatal error
|
||||
Label@DESCB:
|
||||
X: 15
|
||||
Y: 45
|
||||
Width: PARENT_RIGHT - 30
|
||||
Height: 20
|
||||
Font: Regular
|
||||
WordWrap: true
|
||||
Text: The details of the error have been saved to lua.log in the logs directory.
|
||||
Label@DESCC:
|
||||
X: 15
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT - 30
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Please send this file to the map author so that they can fix this issue.
|
||||
@@ -123,7 +123,7 @@ ChromeLayout:
|
||||
cnc|chrome/ingame-infochat.yaml
|
||||
cnc|chrome/ingame-info.yaml
|
||||
cnc|chrome/ingame-infobriefing.yaml
|
||||
cnc|chrome/ingame-infoscripterror.yaml
|
||||
common|chrome/ingame-infoscripterror.yaml
|
||||
cnc|chrome/ingame-infoobjectives.yaml
|
||||
cnc|chrome/ingame-infostats.yaml
|
||||
cnc|chrome/ingame-info-lobby-options.yaml
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Container@SCRIPT_ERROR_PANEL:
|
||||
Height: PARENT_BOTTOM
|
||||
Width: PARENT_RIGHT
|
||||
Logic: ScriptErrorLogic
|
||||
Children:
|
||||
Label@DESCA:
|
||||
X: 15
|
||||
@@ -16,7 +17,7 @@ Container@SCRIPT_ERROR_PANEL:
|
||||
Width: PARENT_RIGHT - 30
|
||||
Height: 20
|
||||
Font: Regular
|
||||
WordWrap: true
|
||||
Align: Center
|
||||
Text: The details of the error have been saved to lua.log in the logs directory.
|
||||
Label@DESCC:
|
||||
X: 15
|
||||
@@ -24,4 +25,15 @@ Container@SCRIPT_ERROR_PANEL:
|
||||
Width: PARENT_RIGHT - 30
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: Please send this file to the map author so that they can fix this issue.
|
||||
ScrollPanel@SCRIPT_ERROR_MESSAGE_PANEL:
|
||||
X: 20
|
||||
Y: 96
|
||||
Width: PARENT_RIGHT - 40
|
||||
Height: 300
|
||||
Children:
|
||||
Label@SCRIPT_ERROR_MESSAGE:
|
||||
X: 4
|
||||
Y: 2
|
||||
Width: PARENT_RIGHT - 32
|
||||
|
||||
Reference in New Issue
Block a user