csol转圈吧 关注:94贴子:752
  • 11回复贴,共1

lua语言学习笔记5 – dofile

取消只看楼主收藏回复

热重载是lua语言程序设计的一个重要思想。与之相关的函数主要包括:load(loadstring)、loadfile、dofile、require,此篇笔记将分别对这些函数进行解析,帮助理解它们的功能及其在代码加载机制中的作用。
烧只因镇楼


IP属地:广东1楼2025-03-08 17:44回复
    需要特别说明的是,以上列出的这些函数并非罗技官方提供的API函数,而是Lua语言自带的一些标准库函数。其中,require函数在罗技驱动的运行环境中已被禁用。这些函数的具体内容以Lua官方文档为准:https://www.lua.org/manual/


    IP属地:广东3楼2025-03-08 17:45
    回复

      load (chunk [, chunkname [, mode [, env]]])
      这个函数在5.2以前lua版本里写作“loadstring”,5.2以后loadstring被移除,写作load。该函数包括四个参数,其中第一个参数是必须的:
      chunk:一个块,它既可以是一个字符串也可以是二进制,(关于二进制码这些问题我发完这篇笔记以后再单独开一篇笔记来讲(shui),这一篇笔记只做简要介绍);
      下面三个参数可以省略:
      chunkname: 模块名,默认是"chunk";
      mode:控制load函数以什么形式加载块:”b”二进制,”t”文本形式,”bt”兼容两种形式,不填默认是bt;
      env:环境变量,不填默认就是用全局变量_ENV或_G填充(这个也开个坑吧,以后再来单独开贴讲这个全局变量的用法);
      这个函数会返回两个结果:func, err
      func:如果load函数执行成功,函数会返回一个可执行函数,如果执行失败,则返回nil(空值);
      err:错误或nil;
      通常情况下,load(loadstring)被用来加载一个文本形式的代码:

      下面对上图代码做一些解释:
      4、5行首先定义了文本a、b,其中a是全局变量,b是局部变量。
      第7~13行定义代码文本code。
      第15行将code传入loadstring中得到编译并返回值到func1与err1;
      第16~20行检查func1非空值后,运行func1函数后如图在控制台输出文本a——这证明了loadstring编译文本时无法读取局部变量,但可以读取全局变量。
      第22~27行继续运行loadstring,但是传入一个无法编译的文本”123;”,我们可以看到在控制台输出了err2的内容:[string "123;"]:1: unexpected symbol near '123'。
      以上便是load(loadstring)的基本用法。


      IP属地:广东4楼2025-03-08 17:45
      回复
        loadfile(filename, mode, env)
        loadfile函数与load(loadstring)类似,实际上它是load(loadstring)的包装函数。在执行时它会先读取文件,然后再执行一次load并返回结果。
        它接受三个参数,其中filename表示文件路径,loadfile将读取filename路径指向的文件并加载。


        IP属地:广东5楼2025-03-08 17:46
        回复

          dofile和以上两个函数类似,实际上dofile也是loadfile的包装函数,在执行该函数时,先执行一次loadfile,然后直接执行编译后的代码,执行失败则直接抛出错误,不会有任何返回:


          IP属地:广东6楼2025-03-08 17:46
          回复

            require与dofile类似,用于加载一个模块(dofile只能加载lua文本文件或者.luac类型的lua二进制码),但是require不会重复加载同一个模块,dofile则在每次运行的时候重新加载一次目标文件。
            因为require函数被罗技禁用,这里不再过多赘述。


            IP属地:广东7楼2025-03-08 17:47
            回复
              那么知道了上面这些内容,对我们有啥用呢?
              一个代码我都写出来了,然后再用另一个函数去加载?是不是吃饱了撑着?
              大部分情况下,如果你只是写一个几百行以内简单的脚本可能还真是这样。所以大部分这些代码就是被一些不愿意让其他人知道自己具体代码怎么写的人用来做“加密”:

              如上图就是一个最简单、最常见的例子,该代码是由Ganlv大佬编写的一个简单异或脚本加密的代码,当然对于了解lua语言的人都知道,这样的加密实际上是不存在任何效果的,这一点我在后续的笔记中会讲到。
              了解我的人都知道我非常反对公开一个脚本但是又去加密他的这种行为,因此这显然不是我要说明的用法。


              IP属地:广东8楼2025-03-08 17:47
              回复
                既然能读取外部文件,那么这给了我们启发,我们就可以按照正常程序设计按模块设计程序,把不同功能的类型的代码分开成几个文件存储,这将大大降低维护的难度。
                如果脚本规模较小,不必拆分代码文件,那么也可以将代码和参数拆分开,下面以我自己写的挂机脚本为例介绍一下我的应用思路:
                首先我将脚本文件拆成四个文件:

                其中,main和code是代码部分,profile和AutoRestart是参数文件。
                main作为程序入口仅用来引导加载另外三个文件并检查代码的正确性,如果检测出问题可以输出相关参数到控制台帮助使用者插出哪里配置有问题。code就是函数主体代码,由utf-8格式存储,保证了控制台能正常输出汉字。
                而配置文件为什么要分开呢?实际上这是出于CSOL挂机常见场景的针对性设计,因为CSOL挂机有相当一部分玩家都不是单挂一个号,事实上很多玩家都是使用虚拟机多开,比如一台电脑开十个号,然后十个号都在同一个房间转圈。
                在这种情况下,这十个号必然有一些参数是需要保持一样的,当然每个号也都有自己不一样的地方,比如键位每个号都不一样。于是我设计虚拟机内部的管理方法,可以先去物理机设置一个局域网共享文件夹,然后在虚拟机里直接用dofile去读物理机的文件。每个号需要保持一致的配置文件可以直接全部都读同一个文件,而每个号独有的文件,可以创建多个不同文件名的文件去分别读取。
                AutoRestart这个参数文件,实际上一开始只是为了配置掉线自动重连而设置,显然这里面就是同一个房间内所有号都需要保持一致的参数,它记录了当前需要重连回去的房号等共享信息,所有号都读取同一个文件,这避免了换一个房就需要重新更改多个配置文件的麻烦。而profile则记录了如键位等私有信息,可以设置多个配置文件每个号只去读取专属自己那一个,或者如果多号设置相同,当然也可以读取同一个。


                IP属地:广东9楼2025-03-08 17:47
                回复
                  对于罗技来说,这些函数的另一个用途就是帮助你正确显示输出信息。那么在这里要先给大家介绍一个关键的计算机常识:文字编码。
                  众所周知,我们所用的计算机中无时无刻发生的其实都是无数0和1的组合,但是要用这些0和1表示汉字这些复杂的文字,就需要人为规定某些特定的组合代表某些字符,因此就需要文字编码。
                  对于一个文本文件来说,最常见的是ANSI和UTF-8这两种编码格式。不同编码系统中相同的字节可能代表不同的字符,或者需要组合多个字节来表示一个字符。如果文件以ANSI编码保存但在UTF-8下读取,计算机可能会错误地将每个字节单独解读为UTF-8中的字符,而不是按照ANSI的多字节规则进行解析。
                  对于罗技的脚本编写来说,大部分版本中,罗技保存lua脚本文字,会以ansi的形式保存,但是控制台输出又会以utf的形式展示给用户,因此这会产生一些问题:

                  可以看见,显然这时候我们就玩不了原神了。
                  因此我们需要用其他方式存储脚本文字,于是我们可以借用dofile这个函数去直接读取我们存储的lua文件:

                  于是我们就成功的玩上了原神。


                  IP属地:广东10楼2025-03-08 17:48
                  回复
                    当然在罗技驱动中,dofile这一类加载外部文件的函数也可以极大简化没有罗技设备或者如虚拟机内部等场景下配置脚本的操作。
                    我们知道,想要配置一个罗技脚本,需要打开它的编写框:

                    但是如果不存在罗技设备,它根本不会让你打开这个界面。解决办法当然有,直接区编辑本地Users里罗技保存的配置文件xml就好了,但是如果你尝试做过这个事情就会知道这相当麻烦,而且xml文件里如果出现大小于符号还需要用转义字符去替换,这非常容易导致错误。
                    但是如果使用dofile方法,那就可以只需要在xml里写一行代码:

                    之后全部都直接拿文本编辑器直接编辑配置文件就好了,不管脚本文件怎么更新,都不需要再去管罗技的驱动。


                    IP属地:广东12楼2025-03-08 17:49
                    收起回复
                      最后回到主题,这篇笔记的第一个词就是热重载。
                      热重载是什么,这实际上是lua语言的一个特点。它允许lua语言在运行的时候不必重新启动程序,就可以重新加载更新的代码或者参数等。
                      对应到实际罗技脚本的编写应用,比如CSOL挂机有如下场景:
                      每天要打8把困难灾变,同时又有鹞子风筝要打,那么你希望零点让它先用魔法刀转城寨,然后等到3点左右直接自己重开一个地狱围栏然后拿着天驱一边跳着一边对着天上打去打鹞子风筝。
                      那么这显然就需要两个不同的配置文件,我们就可以利用热重载的方法去直接加载两个不同的配置文件。
                      具体代码实现不再赘述,有兴趣的可以直接去看我的锟斤拷。


                      IP属地:广东13楼2025-03-08 17:49
                      回复
                        该系列笔记同步上传至百度网盘:
                        链接:https://pan.baidu.com/s/1W-SFxRl_6zThC5iPWEBgjw?pwd=61sm提取码:61sm


                        IP属地:广东14楼2025-03-08 17:49
                        回复