当前位置: 首页 > article >正文

MoonSharp 文档三

MoonSharp 文档一-CSDN博客

MoonSharp 文档二-CSDN博客

MoonSharp 文档四-CSDN博客

MoonSharp 文档五-CSDN博客

7.Proxy objects(代理对象)

如何封装你的实现,同时又为脚本提供一个有意义的对象模型
官方文档:MoonSharp


在实际场景中,脚本通常不受你的控制。这会带来一些问题,其中包括:

安全性:这部分内容不在本页的讨论范围内,但如果你的脚本并非来自严格受控的环境,请务必阅读关于沙箱的部分。

向后和向前兼容性:MoonSharp 会尽力避免引入过去或将来的兼容性问题,但你的 API 设计是你自己的责任!

实现上述目标的一个关键方法是封装你的实现细节,确保脚本不会调用那些不应该调用的 API 字段,这样你就可以自由地调整内部模型,而无需担心意外破坏脚本。

有两种封装方式,一种是通过一些非常复杂的方式复制所有 API,另一种是通过“代理对象”(一种“脚本的 DTO”)。

概念非常简单。对于每个你想封装并暴露给脚本的类型,你需要提供一个“代理类型”,这是一个类,它操作封装(目标)类型的实例。

举个例子胜过千言万语:

// Declare a proxy class
class MyProxy
{MyType target;[MoonSharpHidden]public MyProxy(MyType p){this.target = p;}public int GetValue() { return target.GetValue(); }
}// Register the proxy, using a function which creates a proxy from the target type
UserData.RegisterProxyType<MyProxy, MyType>(r => new MyProxy(r));// Test with a script - only the proxy type is really exposed to scripts, yet everything it works
// just as if the target type was actually used..Script S = new Script();S.Globals["mytarget"] = new MyType(...);
S.Globals["func"] = (Action<MyType>)SomeFunction;S.DoString(@"x = mytarget.GetValue(); func(mytarget); 
");

除了封装之外,代理对象还可以实现一些巧妙的技巧。

其中一个非常简单但非常有用——正确访问值类型。例如,你可以封装 Unity 的 Transform 类(它完全由值类型组成,但本身是引用类型),并通过一个不同的接口来正确保留引用!

8.Error handling(错误处理)

以及错误生成

官方文档:MoonSharp

MoonSharp 会生成几种类型的异常:

  • InternalErrorException

  • SyntaxErrorException

  • ScriptRuntimeException

  • DynamicExpressionException

所有这些异常都继承自一个共同的 InterpreterException 类型,因此可以在异常过滤器中将这些异常归类在一起。

当然,由于调用代码或 MoonSharp 代码中的错误,也可能会生成其他类型的异常,但至少在理论上,这些异常不会由于脚本代码中的错误而产生。

这些异常还支持一个 DecoratedMessage 属性,该属性包含了错误信息,并附带了生成错误的源代码中的引用(如果可用)。

Script.GlobalOptions.RethrowExceptionNested

有一个名为 RethrowExceptionNested 的全局选项。如果设置为 false(默认值),异常会在保留堆栈的情况下重新抛出,并且装饰后的错误信息可以在 DecoratedMessage 属性中获取。如果设置为 true,则会抛出一个新的异常,旧的异常会存储在 InnerException 属性中,并且装饰后的错误信息既可以在 DecoratedMessage 属性中获取,也可以在 Message 属性中获取。

你可以根据个人喜好设置 RethrowExceptionNested,但通常情况下,在 .NET 环境下工作时,设置为 false 效果更好;而在 Mono、Xamarin 和 Unity 环境下工作时,设置为 true 效果更佳。

内部错误异常(InternalErrorException)

当解释器遇到内部错误时,会抛出 InternalErrorException。这种情况通常没有太多可以处理的余地,通常应将其视为脚本引擎的致命错误。如果发生这种情况,请尽可能在论坛或 Discord 中报告,并提供所有可能的信息以便复现问题。

语法错误异常(SyntaxErrorException)

当解析器无法解析脚本代码或脚本代码因某些原因无效时,会抛出 SyntaxErrorException

需要注意的是,此异常是在调用 Script 的某些方法(如 DoFileRunString 等)时抛出的。如果在一个有问题的脚本上调用标准 Lua 库的 load 函数,则会生成一个 ScriptRuntimeException,并将 SyntaxErrorException 包裹在其中。

脚本运行时异常(ScriptRuntimeException)

这是所有异常中最常见的一个,可能也是最重要的一个。

每当 Lua 抛出错误时(无论是通过调用 error 函数,还是因为运行时错误等),都会抛出一个 ScriptRuntimeException。例如:

static void ErrorHandling()
{try{string scriptCode = @"    return obj.calcHypotenuse(3, 4);";Script script = new Script();DynValue res = script.DoString(scriptCode);}catch (ScriptRuntimeException ex){Console.WriteLine("Doh! An error occured! {0}", ex.DecoratedMessage);}
}

将打印:

Doh! An error occured! chunk_1:(2,5-36): attempt to index a nil value

如果我们想向 Lua 抛出一个错误,只需执行相同的操作,抛出一个 ScriptRuntimeException 即可。非常简单。

static void DoError()
{throw new ScriptRuntimeException("This is an exceptional message, no pun intended.");
}static string ErrorGen()
{string scriptCode = @"    local _, msg = pcall(DoError);return msg;";Script script = new Script();script.Globals["DoError"] = (Action)DoError;DynValue res = script.DoString(scriptCode);return res.String;
}

这将返回:

This is an exceptional message, no pun intended.

动态表达式异常(DynamicExpressionException)

当解释器在评估动态表达式时遇到错误时,会抛出 DynamicExpressionException。有关动态表达式的具体含义,请参阅动态表达式的教程。

9.Script Loaders(脚本加载器)

如何更改 MoonSharp 从文件读取脚本的方式

官方文档:MoonSharp

MoonSharp 旨在支持多平台运行。它无法选择将在哪些平台上运行,因为这是库的最终用户的选择,因此它必须以某种方式能够在各种环境中运行,例如作为 Linux 中的守护进程、作为 WPF 应用程序、作为手机上的应用程序或作为游戏主机上的游戏。例如,像 FileStream 这样简单的 API 在 Windows Store 应用中并不可用。

此外,MoonSharp 也不知道它应该如何在使用它的应用程序中运行和工作。例如,如果你希望 loadfile 从嵌入式资源加载脚本而不是从文件加载,该怎么办?

出于这些原因,MoonSharp 提供了两个对象层次结构:

  1. 脚本加载器(Script Loaders) - 用于自定义从文件加载脚本的 API 的工作方式。

  2. 平台访问器(Platform Accessors) - 用于自定义如何完成对操作系统的底层访问。

需要注意的是,通常情况下,脚本加载器与平台访问器是独立的,尽管它们可以根据需要使用对方的方法。这意味着,例如,即使你的平台访问器不支持从文件加载,也不一定意味着你的脚本加载器不能支持,反之亦然。

它们是两个独立的对象,因为它们处理两种不同的职责。脚本加载器负责如何从文件加载脚本,而平台访问器负责处理那些需要调用操作系统 API 的库函数(主要是在 os 和 io 模块中)。

预定义脚本加载程序快速浏览

根据你所使用的平台,你可以选择以下几种脚本加载器:

  1. FileSystemScriptLoader:直接访问文件系统中的文件,可自定义,但在可移植类库(PCL)中不受支持。

  2. ReplInterpreterScriptLoader:与 FileSystemScriptLoader 相同,但额外使用了与 Lua 相同的逻辑从环境变量(如 MOONSHARP_PATHLUA_PATH 或 "?;?.lua",以最先存在的为准)中获取路径。

  3. EmbeddedResourcesScriptLoader:提供对给定程序集的嵌入式资源的访问,而不是文件系统。

  4. InvalidScriptLoader:抛出异常。

  5. UnityAssetsScriptLoader:适用于 Unity3D,用于从文本资源(Text Assets)加载脚本。

如果没有重新定义,MoonSharp 默认使用的脚本加载器按以下规则选择:

  1. 如果在 Unity3D 环境下运行,默认脚本加载器是 UnityAssetsScriptLoader,路径为 Assets/Resources/MoonSharp/Scripts。文件必须具有 .txt 扩展名(因为 Unity 的行为比较特殊)。

  2. 如果当前构建为可移植类库(PCL),则选择 InvalidScriptLoader(除非你采取其他措施,否则无法从文件加载脚本)。

  3. 其他情况下,默认使用 FileSystemScriptLoader

如何指定要使用的脚本加载器

假设我们希望从当前程序集的嵌入资源中加载脚本。

基本上有两种方法,一种是局部方法,另一种是全局方法。 首先,对于给定的脚本,可以通过以下方式修改其脚本加载器:

script.Options.ScriptLoader = new EmbeddedResourcesScriptLoader();

否则,可以使用以下方法指定所有新创建的脚本应使用新的脚本加载程序:

Script.DefaultOptions.ScriptLoader = new EmbeddedResourcesScriptLoader();

如何自定义 require 函数的行为

一个常见的需求是更改 require 函数用于加载模块的目录。

大多数脚本加载器都扩展了 ScriptLoaderBase 类,该类公开了一个 ModulePaths 属性,该属性包含了加载模块时将检查的所有路径。

你可以轻松地更改这些路径:

// These two lines are equivalent:
((ScriptLoaderBase)script.Options.ScriptLoader).ModulePaths = new string[] { "MyPath/?", "MyPath/?.lua" };
// or
((ScriptLoaderBase)script.Options.ScriptLoader).ModulePaths = ScriptLoaderBase.UnpackStringPaths("MyPath/?;MyPath/?.lua");

请注意,ScriptLoaderBase 还会检查当前 _ENV 中的 LUA_PATH 全局变量,以确定使用哪些路径来加载模块。如果你希望忽略 LUA_PATH 全局变量,可以使用:

((ScriptLoaderBase)script.Options.ScriptLoader).IgnoreLuaPathGlobal = true;

谨慎提示:仅仅改变 ModulePaths 和/或 IgnoreLuaPathGlobal 并不足以限制哪些文件可以被加载,形成"沙盒"环境。如果你需要这个功能,请实现一个自定义的脚本加载器。

如何使用 EmbeddedResourcesScriptLoader

将脚本嵌入为资源很容易。

在 Visual Studio 中:

•  在你的项目中创建一个"Scripts"文件夹

•  在该文件夹中添加一个新的文本文件,并将其重命名为 Test.lua

•  在该文件中输入一些 Lua 代码

然后,在解决方案资源管理器中右键单击该文件,会出现以下窗口:

确保 “Build Action” 设置为 “Embedded Resource”。

完成此设置后,我们就使用正确的脚本加载器:

static void EmbeddedResourceScriptLoader()
{Script script = new Script();script.Options.ScriptLoader = new EmbeddedResourcesScriptLoader();script.DoFile("Scripts/Test.lua");
}

在其他 IDE 中也可以遵循类似的步骤,例如 Xamarin。

如何创建自己的脚本加载器

是时候创建你自己的脚本加载器了。

你基本上有两个选择:扩展 ScriptLoaderBase(推荐)或实现 IScriptLoader。

两者都很直观,但为了方便起见,我们还是选择第一个。

我们的脚本加载器实际上会动态生成一个小脚本,其名称是请求的文件名,而不是真正地去加载文件:

private class MyCustomScriptLoader : ScriptLoaderBase
{public override object LoadFile(string file, Table globalContext){return string.Format("print ([[A request to load '{0}' has been made]])", file);}public override bool ScriptFileExists(string name){return true;}
}

实现这一点很容易:

static void CustomScriptLoader()
{Script script = new Script();script.Options.ScriptLoader = new MyCustomScriptLoader() { ModulePaths = new string[] { "?_module.lua" } };script.DoString(@"require 'somemodule'f = loadfile 'someothermodule.lua'f()");
}

运行这些操作将打印:

A request to load 'somemodule_module.lua' has been made
A request to load 'someothermodule.lua' has been made

end

相关文章:

MoonSharp 文档三

MoonSharp 文档一-CSDN博客 MoonSharp 文档二-CSDN博客 MoonSharp 文档四-CSDN博客 MoonSharp 文档五-CSDN博客 7.Proxy objects&#xff08;代理对象&#xff09; 如何封装你的实现&#xff0c;同时又为脚本提供一个有意义的对象模型 官方文档&#xff1a;MoonSharp 在实际…...

linux和windows之间的复制

第一步 sudo apt-get autoremove open-vm-tools第二步 sudo apt-get update第三步 sudo apt-get install open-vm-tools-desktop按y 第四步 重启虚拟机&#xff0c;终端下输入 rebootLinux下 按“ CtrlShiftC V ”复制粘贴 Windows下按“ Ctrl C V ”复制粘贴...

在资源有限中逆势突围:从抗战智谋到寒门高考的破局智慧

目录 引言 一、历史中的非对称作战&#xff1a;从李牧到八路军的智谋传承 李牧戍边&#xff1a;古代军事博弈中的资源重构 八路军的游击战&#xff1a;现代战争中的智慧延续 二、创业界的逆袭之道&#xff1a;小米与拼多多的资源重构 从MVP到杠杆解 社交裂变与资源错配 …...

Ubuntu 22.04 无法进入图形界面的解决方法

Ubuntu 22.04 无法进入图形界面&#xff0c;只能进入 tty&#xff0c;可能是由于图形界面相关的配置或驱动程序出现了问题。以下是一些常见的解决方法&#xff1a; 1. 检查图形界面服务状态 首先&#xff0c;检查图形界面服务&#xff08;通常是 gdm 或 lightdm&#xff09;的…...

Python中很常用的100个函数整理

Python 内置函数提供了强大的工具&#xff0c;涵盖数据处理、数学运算、迭代控制、类型转换等。本文总结了 100 个常用内置函数&#xff0c;并配备示例代码&#xff0c;提高编程效率。 1. abs() 取绝对值 print(abs(-10)) # 10 2. all() 判断所有元素是否为真 print(all([…...

javascript-es6 (六)

编程思想 面向过程 面向过程就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候再一个一个的依次 调用就可以了 就是按照我们分析好了的步骤&#xff0c;按照步骤解决问题 面向对象 面向对象是把事务分解成为一个个对象&…...

大模型微调技术基础(一)

文章目录 GPT与BERT的差异GPT&#xff08;Decoder架构&#xff09;优点缺点 BERT&#xff08;Encoder架构&#xff09;优点缺点 总结 LoRA低参数大模型与全参数小模型表现对比分析LoRA&#xff08;Low-Rank Adaptation&#xff09;技术详解1. LoRA 核心原理2. 应用场景3. 简单代…...

Spring AI 1.0.0 M6新特性MCP

Spring AI 1.0.0 M6新特性MCP 前言一、MCP是什么&#xff1f;&#xff08;Model Context Protocol&#xff09;二、它的发展历程三、核心架构四、MCP Java SDK的核心能力Java MCP实现遵循三层架构&#xff1a;MCP客户端MCP服务器总结MCP 的核心能力总结多种传输选项 搭建服务端…...

【时时三省】(C语言基础)赋值表达式和赋值语句和变量赋初值

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 赋值表达式和赋值语句 在C程序中&#xff0c;赋值语句是用得最多的语句。实际上&#xff0c;C语言的赋值语句属于表达式语句&#xff0c;由一个赋值表达式加一个分号组成。其他一些高级语言…...

Room数据库的使用

一、room的引用导入 1、在app的gradle中引入 plugins {//这个ksp 一定要对应相关的 kotlin 版本&#xff0c;不然会一直报错i的---id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false } 2、在model的gradle中引入 plugins {id("com.g…...

【性能测试入门_01性能测试jmeter基础实操场景详解】

一、应用项目如何部署在服务器上 可以将项目进行打jar包 双击install&#xff0c;控制台就会打印打包的过程 最终打的包&#xff0c;会存放在打印的那个路径下 这个jar包&#xff0c;就是开发人员开发好&#xff0c;直接可以部署的 可以通过命令&#xff0c;在终端直接启动这…...

SpringBoot加载配置文件的优先级

在 SpringBoot 应用中&#xff0c;配置文件的加载顺序&#xff08;优先级&#xff09;决定了不同来源的配置属性如何覆盖彼此&#xff0c;但是&#xff0c;在实践中&#xff0c;也会经常遇到。下面文章将分析 SpringBoot 配置文件加载的优先级&#xff0c;从高到低排列。 文章目…...

跨越时空的对话:图灵与GPT-4聊AI的前世今生

&#xff08;背景&#xff1a;虚拟咖啡厅&#xff0c;图灵身着1950年代西装&#xff0c;端着一杯热茶&#xff0c;GPT-4以全息投影形态坐在对面&#xff09; 图灵&#xff08;喝了口茶&#xff09;&#xff1a;“听说你能写诗&#xff1f;我当年在布莱切利园破解Enigma时&…...

如何通过 Seatunnel 实现 MySQL 到 OceanBase的数据迁移同步

1. 准备传输工具 本方案采用 Apache Seatunnel&#xff08;简称seatunnel&#xff09;进行MySQL 到 OceanBase 的数据迁移和同步&#xff0c;出于对方案轻量性的考量&#xff0c;我们采用其内置的Zeta引擎来实现&#xff0c;包括全量同步、离线增量同步&#xff0c;以及CDC方案…...

C++20 新特性总结

简要总结 C20 引入了四项非常大的更新, 分别是: 概念(Concepts). 用来简化模板编程, 强化表达能力. 并且使得出错原因更容易查找.模块(Modules). 这是代码组织方面非常大的更新. 提供了新的方式来组织代码, 并且可以减少编译时间.范围库(Ranges and Views). 轻量级的, 非拥有…...

软件设计模式之简单工厂模式

目录 一.类图&#xff08;手机生产&#xff09; 二.代码实现 Iphone类&#xff1a; Vivo类&#xff1a; Mobile类&#xff1a; MobileFactory类&#xff1a; Client类&#xff1a; 一.类图&#xff08;手机生产&#xff09; 补充&#xff1a;MobileFactory也可以直接指向…...

内网激活JRebel插件(无网络环境)

1.官网下载安装包,JRebel and XRebel JRebel and XRebel - IntelliJ IDEs Plugin | Marketplace 2.以IInstall Plugin from Disk的方式读取 3.运行JrebelServer.jar 终端输入&#xff1a; java -jar JrebelServer.jar -p 8080 (默认8080端口)服务会自动打开浏览器至 http:/…...

LiveGBS流媒体平台GB/T28181常见问题-视频流安全控制HTTP接口鉴权勾选流地址鉴权后401Unauthorized如何播放调用接口流地址校验

LiveGBS流媒体平台GB/T28181常见问题频流安全控制HTTP接口鉴权勾选流地址鉴权后401Unauthorized如何播放调用接口流地址校验&#xff1f; 1、安全控制1.1、HTTP接口鉴权1.2、流地址鉴权 2、401 Unauthorized2.1、携带token调用接口2.1.1、获取鉴权token2.1.2、调用其它接口2.1.…...

Java 大视界 -- 区块链赋能 Java 大数据:数据可信与价值流转(84)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

接口自动化入门 —— Http的请求头,请求体,响应码解析!

在接口自动化测试中&#xff0c;HTTP请求头、请求体和响应码是核心组成部分。理解它们的作用、格式和解析方法对于进行有效的接口测试至关重要。以下是详细解析&#xff1a; 1. HTTP 请求头&#xff08;Request Header&#xff09; 1.1 作用 请求头是客户端向服务器发送的附加…...

upload-labs(1-20)详解(专业版)

目录 第1关 第2关 第3关 第4题 第5题 第6题 第7题 第8题 第9题 第10题 第11题 第12题 第13题 第1关 查看源码 在第一关是一个前端js的一个后缀识别&#xff1a;当不为jpg、png、gif时候出现弹窗 检查源码 将return checkFile() 改为 return ture 就可以将php顺利…...

Linux 生成静态库

文章目录 前提小知识生成和使用.a库操作步骤 在应用程序中&#xff0c;有一些公共的代码需要反复使用的&#xff0c;可以把这些代码制作成“库文件”&#xff1b;在链接的步骤中&#xff0c;可以让链接器在“库文件”提取到我们需要使用到的代码&#xff0c;复制到生成的可执行…...

ARMV8的64位指令

一、介绍 ARMv8 体系结构最大的改变是增加了一个新的 64 位的指令集&#xff0c;这是早前 ARM 指令集 的有益补充和增强。它可以处理 64 位宽的寄存器和数据并且使用 64 位的指针来访问内存。这 个新的指令集称为 A64 指令集&#xff0c;运行在 AArch64 状态。 ARMv8 兼容旧的…...

【git】 贮藏 stash

贮藏是我在sourcetree上看到的名词。之前只是浅浅的用来收藏一下修改的文件&#xff0c;没有完整的使用过。今天有幸使用了一次就来展开说说。 使用原因就不赘述了&#xff0c;错误的操作少提为好&#xff0c;操作步骤如下&#xff1a; 查看贮藏列表git stash list #输出&…...

ctf-web: php原生类利用 -- GHCTF Popppppp

源代码 <?php error_reporting(0); class CherryBlossom { public $fruit1; public $fruit2; public function __construct($a) {$this->fruit1 $a; } function __destruct() { echo $this->fruit1; } public function __toString() { $newFunc …...

PawSQL for TDSQL:腾讯云TDSQL数据库性能优化全攻略

TDSQL 作为腾讯云推出的分布式数据库&#xff0c;凭借其高扩展性、高可用性和高性能等优势&#xff0c;广泛应用于金融、互联网、政务等领域。随着业务的不断增长和数据量的爆炸式增长&#xff0c;如何优化 TDSQL 数据库的性能&#xff0c;成为众多企业和开发者面临的挑战。本文…...

202250311-WINDOWS本地4G显存Docker运行vLLM

前置&#xff1a; 需要去huggingface注册账号获取token&#xff1a;HUGGING_FACE_HUB_TOKEN 运行vLLM docker run --name LocalvLLM_qwen1.5B_Int4 --runtime nvidia --gpus all -v D:/vLLM/.cache/huggingface:/root/.cache/huggingface --env "HUGGING_FAC…...

Scala 中生成一个RDD的方法

在 Scala 中&#xff0c;生成 RDD&#xff08;弹性分布式数据集&#xff09;的主要方法是通过 SparkContext&#xff08;或 SparkSession&#xff09;提供的 API。以下是生成 RDD 的常见方法&#xff1a; 1. 从本地集合创建 RDD 使用 parallelize 方法将本地集合&#xff08;如…...

T-SQL 语言基础:表运算符与联接

目录 介绍表运算符概述交叉联接内联接外联接联接实例总结引用 1. 介绍 在这篇博客中&#xff0c;主要涉及 T-SQL 中的表运算符与联接。联接操作是 SQL 查询中最常用的操作之一&#xff0c;它允许我们在多个表之间进行数据关联。通过了解不同类型的联接及其应用场景&#xff…...

Electron打包工具对比

在 Electron 生态中&#xff0c;打包工具的选择直接影响开发效率、配置复杂度和最终应用的性能。以下是主流的 Electron 打包工具及其优劣分析&#xff0c;结合你的 Vue 项目需求&#xff0c;我会在最后给出推荐方案&#xff1a; 一、主流 Electron 打包工具对比 1. Electron …...