Unity中 Xlua使用整理(二)

1.Xlua的配置应用

xLua所有的配置都支持三种方式:打标签静态列表动态列表
配置要求
列表方式均必须是static的字段/属性
列表方式均必须放到一个static类
建议不用标签方式
建议列表方式配置放Editor目录(如果是Hotfix配置,而且类位于Assembly-CSharp.dll之外的其它dll,必须放Editor目录)

1.打标签

xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,该方式方便,但在il2cpp下会增加不少的代码量,不建议使用

1.LuaCallCSharp

一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法)。一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了LuaCallCSharp配置,则执行父类的适配代码。如果没有适配代码则会尝试用反射来访问,但反射访问性能不佳,在il2cpp下有可能因为代码剪裁而导致无法访问。

1.打标签

[LuaCallCSharp]
public class TestObj
{
    public void test(testStruct t)
    {
        Debug.Log($"{t.a},{t.c}");
    }
}

2.静态列表

   [LuaCallCSharp]
   public static List<Type> luaCallCsharp = new List<Type>
   {
       typeof(TestObj),
   };

  上面的类加了此标签,就会生成适配代码

 2.ReflectionUse

一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。对于扩展方法,必须加上 LuaCallCSharp 或者 ReflectionUse 才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。

如何配置?

[ReflectionUse]
public class TestReflectionUseObj
{

}

link.xml保存着打上LuaCallCSharp和ReflectionUse标签的类

3.DoNotGen

指明一个类里头的部分函数、字段、属性不生成代码,通过反射访问。
只能标准 Dictionary<Type, List<string>> 的field或者property。key指明的是生效的类,value是一个列表,配置的是不生成代码的函数、字段、属性的名字。

如何配置?

[LuaCallCSharp]
public class TestDontGen
{
    public int a1;

    public int a2;
}

1.打标签(这种方式行不通)

在生成代码中,a2还是生成和a1一样的适配函数

2.静态列表

public static class ExportCfg
{
    [DoNotGen]
    public static Dictionary<Type,List<string>> dontGenDic = new Dictionary<Type, List<string>> {
        [typeof(TestDontGen)] = new List<string> { "a2" },
    };
}

4.CSharpCallLua

把一个lua函数适配到一个C# delegate(一类是C#侧各种回调:UI事件,delegate参数,比如List<T>:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate),或者把一个lua table适配到一个C# interface时,使用该标签。

如何配置?

1.打标签:

   [CSharpCallLua]
   public interface IPlayerPosition
   {
       int x { get; set; }
       int y { get; set; }
       int z { get; set; }

       void add(int x, int y, int z);
       void sub(int x, int y, int z);
   }

   [CSharpCallLua]
   public delegate void AddMethod(LuaTable self, int x, int y, int z);

   [CSharpCallLua]
   public delegate Action addAc(LuaTable t, int x, int y, int z);

    [CSharpCallLua]
    public delegate void test1(int x);

    [CSharpCallLua]
    public delegate Action test2(int x);

2.静态列表:

[CSharpCallLua]
public static List<Type> CsharpCallLua = new List<Type> { 
   typeof(IPlayerPosition),
   typeof(AddMethod),
   typeof(addAc),
   typeof(test1),
   typeof(test2),
};

 生成代码如下:接口生成独立文件,委托事件生成在DelegateGenBridge文件中

 

5.GCOptimize

xLua无参构造函数的复杂类型(struct)的默认传递方式是引用传递,当lua侧使用完毕释放该对象的引用时,会产生一次gc。
一个C#纯值类型(注:指的是一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者C#枚举值加上这个标签之后,XLua会生成gc优化代码,在lua和c#间传递将不产生(C#)gc alloc,该类型的数组(一维?)访问也不产生gc。

如果没有配置该标签的值类型传递

public class Test{   
    [CSharpCallLua]
    public delegate Action testAction(TestGCOptimizeValue x);

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.AddLoader(customLoader);
        luaenv.DoString("require 'main'");
        ta = luaenv.Global.Get<testAction>("testAction");
    }

    private void Update()
    {
        if (Application.isPlaying)
        {
            ta?.Invoke(ts);
        }
    }
}

public struct TestGCOptimizeValue
{
    public int a;
    public int b;
    private float f;
    //[AdditionalProperties]
    public float F { get => f; set => f = value; }
}

生成的代码如下:

将struct类型装箱转换为object再进行处理。

如何配置?

[GCOptimize]
public struct TestGCOptimizeValue
{
    public int a;
    public int b;
    public float f;
}

 加上此标签后,就会在PackUnpack中生成适配代码

 

然后调用函数生成代码如下:

struct 满足条件如下:

1.struct允许嵌套其它struct,但它以及它嵌套的struct只能包含这几种基本类型:byte、sbyte、short、ushort、int、uint、long、ulong、float、double;例如UnityEngine定义的大多数值类型:Vector系列,Quaternion,Color。。。均满足条件,或者用户自定义的一些struct
2.该struct配置了GCOptimize属性(对于常用的UnityEngine的几个struct,Vector系列,Quaternion,Color。。。均已经配置了该属性),这个属性可以通过配置文件或者C# Attribute实现;
3.使用到该struct的地方,需要添加到生成代码列表;

6.AdditionalProperties

该标签GCOptimize的扩展配置,如果struct有的字段是私有的,需要通过属性来访问,这时就需要用到该配置(默认情况下GCOptimize只对public的field打解包)

如何配置?

如果不加此标签

[GCOptimize]
public struct TestGCOptimizeValue
{
    public int a;
    public int b;
    private float f;
    public float F { get => f; set => f = value; }
}

生成代码如下:无法生成访问f字段的代码

1.打标签方式

[GCOptimize]
public struct TestGCOptimizeValue
{
    public int a;
    public int b;
    private float f;
    [AdditionalProperties]
    public float F { get => f; set => f = value; }
}

 2.静态列表

  [AdditionalProperties]
  static Dictionary<Type, List<string>> AdditionalProperties = new Dictionary<Type, List<string>>
  {
      [typeof(TestGCOptimizeValue)] = new List<string> { "F" },
      
  };

生成代码如下:

7.BlackList

一个类型的一些成员不需要适配代码,可以通过加上该标签来实现。考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是 List<List<string>>,对于每个成员,在第一层List有一个条目,第二层List是个string的列表,第一个string是类型的全路径名第二个string是成员名如果成员是一个方法,还需要从第三个string开始,把其参数的类型全路径全列出来

如何配置?

[LuaCallCSharp]
public class TestBlackList
{
    public int a1;
    public int a2;

    public void Test()
    {

    }
}

若不加处理生成代码如下: 

1.打标签

C#代码:

[LuaCallCSharp]
public class TestBlackList
{
    public int a1;
    [BlackList]
    public int a2;

    [BlackList]
    public void Test()
    {

    }
}

生成代码如下: 

2.静态列表

列表中的第一个字符串是类型,第二个及以后是要屏蔽的字段

  [BlackList]
  public static List<List<string>> BlackList = new List<List<string>>()
  {
      new List<string>(){ "TestBlackList", "a2"},
      new List<string>(){ "TestBlackList", "Test"}
  };

生成代码:

注:
DoNotGen和ReflectionUse的区别是:1、ReflectionUse指明的是整个类;2、当第一次访问一个函数(字段、属性)时,ReflectionUse会把整个类都wrap,而DoNotGen只wrap该函数(字段、属性);
DoNotGen和BlackList的区别是:1、BlackList配了就不能用;2、BlackList能指明某重载函数,DoNotGen不能;

2.静态列表

有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时可以在一个静态类里声明一个静态字段,然后为这字段加上标签,并且这个字段需要放到一个静态类里,建议放到 Editor目录。

如何配置?

public static class ExportCfg
{
    [DoNotGen]
    public static Dictionary<Type, List<string>> dontGenDic = new Dictionary<Type, List<string>>
    {
        [typeof(TestDontGen)] = new List<string> { "a2" },
    };

    [BlackList]
    public static List<List<string>> BlackList = new List<List<string>>()
    {
        new List<string>(){ "TestBlackList", "a2"},
        new List<string>(){ "TestBlackList", "Test"}
    };

    [AdditionalProperties]
    public static Dictionary<Type, List<string>> AdditionalProperties = new Dictionary<Type, List<string>>
    {
        [typeof(TestGCOptimizeValue)] = new List<string> { "F" },
        
    };

    [LuaCallCSharp]
    public static List<Type> luaCallCsharp = new List<Type>
    {
        typeof(TestObj),
    };

    [CSharpCallLua]
    public static List<Type> CsharpCallLua = new List<Type> { 
       typeof(IPlayerPosition),
       typeof(AddMethod),
       typeof(addAc),
       typeof(test1),
       typeof(test2),
    };
}

3.动态列表

声明一个静态属性,打上相应的标签即可。

见HotFix。

2.HotFix

1.标识要热更新的类型

1.打标签:(不建议,在高版本 Unity 不支持)

[Hotfix]
public class TestHotFix
{
    public void TestPrint()
    {
        Debug.Log("Before Hotfix");
    }
}

2.static列表:(高版本 Unity 需要把配置文件放 Editor 目录下)

[Hotfix]
public static List<Type> by_property
{
     get
     {
        return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
              where type.Namespace == "XXXX"
              select type).ToList();
     }
}

[Hotfix]
public static List<Type> by_field = new List<Type>()
{
      typeof(HotFixSubClass),
      typeof(GenericClass<>),
};

2.API

1.xlua.hotfix(class, [method_name], fix)

class :C#类,CS.Namespace.TypeName或者字符串方式"Namespace.TypeName"表示,字符串格式和C#的Type.GetType要求一致,如果是内嵌类型(Nested Type)是非Public类型,只能用字符串方式表示"Namespace.TypeName+NestedTypeName";
method_name : 方法名,可选;
fix : 如果传了method_name,fix将会是一个function,否则通过table提供一组函数。table的组织按key是method_name,value是function的方式。

2.base(csobj)

子类override函数通过base调用父类实现。
csobj : 对象
返回值 : 新对象,可以通过该对象base上的方法

3.util.hotfix_ex(class, method_name, fix)

可以在fix函数里头执行原来的函数缺点是fix的执行会略慢。
method_name : 方法名;
fix : 用来替换C#方法的lua function

Lua侧示例:

xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
    print('BaseTest', p)
    base(self):Foo(p)
end)

3.Hotfix Flag

使用方式:

[Hotfix(HotfixFlag.xxxx)]
public class TestHotFixClass
{

}
1.Stateless、Stateful

遗留设置,Stateful 方式在新版本已经删除,使用xlua.util.state代替,默认为Stateless。
这个东西不知道是个啥玩意,从utile.state的代码和Xlua例子的代码来看,就是可以为热修复类新增一些属性事件类似的东西,在构造函数中指给Lua侧的当前类(table)一个父类(metatable)。

debug.setmetatable:

 rawget,rawset:获取或设置table的值,不从原表获取或者设置

Lua 5.4 Reference Manual

使用方式:

xlua.hotfix(CS.TestHotFixClass, {  
      ['.ctor'] = function(self)
            util.state(self, {evt = {}})
      end;
})
2.ValueTypeBoxing

值类型的适配delegate会收敛到object(这是个啥?),好处是代码量更少,不好的是值类型会产生装箱,适用于对字段敏感的业务。

就是在生成delegate的时候,

如果是HotFix标签中包含ValueTypeBoxing,就会将方法和参数的类型转换为object类型

3.IgnoreProperty

不对属性注入及生成适配代码。

4.IgnoreNotPublic

不对非public的方法注入及生成适配代码。

5.Inline

不生成适配 delegate,直接在函数体注入处理代码。

6.IntKey

不生成静态字段,而是把所有注入点放到一个数组集中管理。

4.打补丁

Xlua可以用lua函数替换 C# 的构造函数,函数,属性,事件。

1.函数

method_name 传函数名,支持重载,不同重载都是转发到同一个 lua 函数。

见Unity中 Xlua使用整理(一)_为xlua配置搜索路径-CSDN博客中的HotFix章节

2.构造函数

构造函数对应的 method_name 是 ".ctor"。和普通函数不一样的是,构造函数的热补丁是执行原有逻辑后调用lua。

C#侧代码:

[Hotfix]
[LuaCallCSharp]
public class TestHotFixClass
{
    public TestHotFixClass(){
        Debug.Log("c# .ctor");
    }

    public TestHotFixClass(int p, int c)
    {
        Debug.Log($"C# .ctor {p} , {c}");
    }
}

调用代码:

//调用代码:
luaenv.DoString("require 'main'");
TestHotFixClass testHotFixClass = new TestHotFixClass();
TestHotFixClass testHotFixClass1 = new TestHotFixClass(10, 20);

Lua侧代码:

xlua.hotfix(CS.TestHotFixClass, '.ctor' , function(self,p,c,d)
        print("Lua .ctor",p,c,d)
end)

结果:

 

3.属性

method_name 等于 get_属性名,setter 的 method_name 等于 set_属性名。

C#侧代码:

[Hotfix]
[LuaCallCSharp]
public class TestHotFixClass
{
    private int testProperty;

    public int TestProperty{ 
        get {
            return testProperty;
        } 
        set {
            testProperty = value;
            Debug.Log($"c# set {testProperty}");
        } 
    }
}

调用代码:

  TestHotFixClass testHotFixClass = new TestHotFixClass();
  luaenv = new LuaEnv();
  luaenv.AddLoader(customLoader);
  testHotFixClass.TestProperty = 10;
  Debug.Log($"before get {testHotFixClass.TestProperty}");
  luaenv.DoString("require 'main'");
  Debug.Log($"after get {testHotFixClass.TestProperty}");
  testHotFixClass.TestProperty = 100;
  Debug.Log($"set after get {testHotFixClass.TestProperty}");

Lua侧代码:

xlua.hotfix(CS.TestHotFixClass, {  
    set_TestProperty = function(self, v)
        self.testProperty = v
        print('Lua set_TestProperty', v)
    end;
    get_TestProperty = function(self)
        print('Lua get_TestProperty')
        return self.testProperty
    end
})

结果:

 

4.[]操作符

赋值对应 set_Item,取值对应 get_Item。第一个参数是 self,赋值后面跟 key,value,取值只有 key 参数,返回值是取出的值。

C#侧代码:

 public int this[string field]
 {
     get{ return 1;}
     set{}
 }

 public string this[int index]
 {
     get{ return "aaabbbb";}
     set{}
 }

C#侧调用代码:

 Debug.Log(testHotFixClass[1]);
 Debug.Log(testHotFixClass["cc"]);
 luaenv.DoString("require 'main'");
 Debug.Log(testHotFixClass[1]);
 Debug.Log(testHotFixClass["cc"]);

Lua侧代码:

xlua.hotfix(CS.TestHotFixClass, {  
     get_Item = function(self, k)
         print('get_Item', k)
         return 1024
     end;
     set_Item = function(self, k, v)
         print('set_Item', k, v)
     end;
})

结果:

5.其他操作符

6.事件

+= 操作符是 add_事件名-=对应的是 remove_事件名。这两个函数均是第一个参数是self,第二个参数是操作符后面跟的delegate。

C#代码:

[Hotfix]
[LuaCallCSharp]
public class TestHotFixClass
{
    public event Action myEvent;
}

调用代码:

    luaenv.DoString("require 'main'");
    TestHotFixClass testHotFixClass = new TestHotFixClass();
    testHotFixClass.myEvent += TestHotfixEvent;
    testHotFixClass.myEvent -= TestHotfixEvent;

Lua代码:

-- 事件
xlua.hotfix(CS.TestHotFixClass, {  
      ['.ctor'] = function(self)
            util.state(self, {evt = {}})
      end;
      add_myEvent = function(self, cb)
          print('add_Event', cb)
          table.insert(self.evt, cb)
      end;
      remove_myEvent = function(self, cb)
          print('remove_Event', cb)
          for i, v in ipairs(self.evt) do
             if v == cb then
                table.remove(self.evt, i)
             break
             end
         end
     end;
})

结果:

 

通过 xlua.private_accessible(版本号大于2.1.11不需要调用 xlua.private_accessible)来直接访问,事件对应的私有 delegate 的直接访问后,可以通过对象的"&事件名"字段直接触发事件,例如 self['&MyEvent'](),其中MyEvent是事件名。

7.析构函数

method_name 是 "Finalize",传一个 self 参数。析构函数的热补丁并不是替换,而是开头调用 lua 函数后继续原有逻辑。

C#侧代码:

[Hotfix]
[LuaCallCSharp]
public class TestHotFixClass
{
    ~TestHotFixClass()
    {
        Debug.Log("执行析构函数");
    }
}

调用代码:

     TestHotFixClass testHotFixClass = new TestHotFixClass();
     luaenv = new LuaEnv();
     luaenv.AddLoader(customLoader);
     System.GC.Collect();
     System.GC.WaitForPendingFinalizers();
     luaenv.DoString("require 'main'");
     TestHotFixClass testHotFixClass1 = new TestHotFixClass();
     luaenv.FullGc();
     System.GC.Collect();
     System.GC.WaitForPendingFinalizers();

Lua侧代码:

xlua.hotfix(CS.TestHotFixClass,"Finalize", function(self)
     print('Finalize', self)
end)

结果:

 

注:System.GC.Collect()

 System.GC.WaitForPendingFinalizers

luaenv.FullGC():清理GC

8.泛型

其他规则一致,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。

C#代码:

[Hotfix]
public class TestGeneric<T>
{
    public TestGeneric()
    {
        Debug.Log($"aaaaa,{typeof(T)}");
    }
}

调用代码:

TestGeneric<int> testGeneric = new TestGeneric<int>();
TestGeneric<float> testGeneric1 = new TestGeneric<float>();
luaenv.DoString("require 'main'");
TestGeneric<int> testGeneric2 = new TestGeneric<int>();
TestGeneric<float> testGeneric3 = new TestGeneric<float>();

Lua代码:

xlua.hotfix(CS.TestGeneric(CS.System.Int32),".ctor", function(self)
     print("LUA INT32")
end)

xlua.hotfix(CS.TestGeneric(CS.System.Single),".ctor", function(self)
     print("LUA FLAOTG4")
end)

结果:

如果Lua侧没有泛型的单独函数不会报错,只会执行C#侧的泛型函数

TestGeneric<double> testGeneric2 = new TestGeneric<double>();

 

9.Unity协程

通过 util.cs_generator 可以用一个 function 模拟一个 IEnumerator,yield return使用在里头的 coroutine.yield模拟。

C#代码:

[Hotfix]
public class HotFixSubClass : MonoBehaviour
{
    IEnumerator Start()
    {
        while (true)
        {
            yield return new WaitForSeconds(3);
            Debug.Log("C# Wait for 3 seconds");
        }
    }
}

调用代码:

 Unity协程的使用请看3.1节。

C#代码:

[Hotfix]
public class HotFixCoroutine: MonoBehaviour
{
    private void Start()
    {
        StartCoroutine(CoroutineStart());
    }

    IEnumerator CoroutineStart()
    {
        while (true)
        {
            yield return new WaitForSeconds(1);
            Debug.Log("c# Wait for 3 seconds");
        }
    }
}

调用代码:

new GameObject().AddComponent<HotFixCoroutine>();
luaenv.DoString("require 'main'");
new GameObject().AddComponent<HotFixCoroutine>();

Lua代码:

xlua.hotfix(CS.HotFixCoroutine,{
        CoroutineStart = function(self)
            return util.cs_generator(function()
                while true do
                    coroutine.yield(CS.UnityEngine.WaitForSeconds(3))
                    print('Wait for 3 seconds')
                end
            end)
        end;
})

结果:

10.整个类

如果要替换整个类,使用一个 table,按 method_name = function 组织即可。

注:将打补丁1-9小节挑选合适的填入table({})中

xlua.hotfix(CS.类名, {  
     --放入要热修复的代码
})

3.其他

 1.Unity协程的使用

C#代码:

新建一个MonoBehaviour,

   public class TestCoroutine : MonoBehaviour
   {
   }

添加WaitForSeconds到LuaCallCSshrap的静态列表中,供Lua侧调用

 

Lua代码:

local gameobject = CS.UnityEngine.GameObject('CoroutineTest')
CS.UnityEngine.Object.DontDestroyOnLoad(gameobject)
local cs_coroutine_runner = gameobject:AddComponent(typeof(CS.TestCoroutine))

local times = 5
cs_coroutine_runner:StartCoroutine(util.cs_generator(function()
    while (times > 0) do
       print('coroutine aaaaa')
	   coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
       times = times - 1
    end
end))

结果:

 

 2.Async的使用

未完待续。。。

参考链接:

介绍 — XLua (tencent.github.io)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/952004.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】课程 实验二 交换机基本配置和VLAN 间路由实现

实验二 交换机基本配置和VLAN 间路由实现 一、实验目的 1&#xff0e;了解交换机的管理方式。 2&#xff0e;掌握通过Console接口对交换机进行配置的方法。 3&#xff0e;掌握交换机命令行各种模式的区别&#xff0c;能够使用各种帮助信息以及命令进行基本的配置。 4&…

【数据结构:前缀树Trie】

目录 前言前缀树介绍和应用一、前缀树的定义前缀树的问题和思考前缀树的映射思想前缀树三大性质 二.前缀树节点结构三. 前缀树接口介绍和实现四个接口API1. insert(String word)2. search(String word)3. startsWith(String pre)4. delete(String word) API实现1. 查询操作sear…

Jenkins触发器--在其他项目执行后构建

前言&#xff1a; jenkins中有多种触发器可用&#xff0c;可以方便的控制构建的启动 这里简单介绍下项目后构建的配置方法 1. 解释&#xff1a; Build after other projects are built Set up a trigger so that when some other projects finish building, a new build is…

Linux(18)——提高命令行运行效率

目录 一、创建和执行 shell 脚本&#xff1a; 1、命令解释器&#xff1a; 2、执行 Bash Shell 脚本&#xff1a; 3、从 shell 脚本提供输出&#xff1a; 二、对特殊字符加引号&#xff1a; 1、反斜杠 &#xff08;\&#xff09;&#xff1a; 2、单引号 &#xff08; &…

软件系统安全逆向分析-混淆对抗

1. 概述 在一般的软件中&#xff0c;我们逆向分析时候通常都不能直接看到软件的明文源代码&#xff0c;或多或少存在着混淆对抗的操作。下面&#xff0c;我会实践操作一个例子从无从下手到攻破目标。 花指令对抗虚函数表RC4 2. 实战-donntyousee 题目载体为具有漏洞的小型软…

计算机网络 (33)传输控制协议TCP概述

一、定义与基本概念 TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在OSI模型的第四层&#xff0c;即传输层&#xff0c;为用户提供可靠的、有序的和无差错的数据传输服务。TCP协议与UDP协议是传输层的两大主要协议&#xff0c;但两者在设计上有明显的不同&…

【从0带做】基于Springboot3+Vue3的高校食堂点餐系统

大家好&#xff0c;我是武哥&#xff0c;最近给大家手撸了一个基于SpringBoot3Vue3的高校食堂点餐系统&#xff0c;可用于毕业设计、课程设计、练手学习&#xff0c;系统全部原创&#xff0c;如有遇到网上抄袭站长的&#xff0c;欢迎联系博主~ 详细介绍 https://www.javaxm.c…

一文说清dockerfile编写

docker用的时间比较久了&#xff0c;关于怎样把jar打成镜像&#xff0c;怎样基于已有mysql镜像添加额外初始化后封装成新的镜像&#xff0c;进行简单的说明。 1.jar封装镜像 from centos # 设置本地为中文&#xff0c;解决中文乱码问题 RUN localedef -i zh_CN -f UTF-8 zh_CN…

基于Python实现的通用小规模搜索引擎

基于Python实现的通用小规模搜索引擎 1.项目简介 1.1背景 《信息内容安全》网络信息内容获取技术课程项目设计 一个至少能支持10个以上网站的爬虫程序&#xff0c;且支持增量式数据采集;并至少采集10000个实际网页;针对采集回来的网页内容&#xff0c; 能够实现网页文本的分…

ssm旅游攻略网站设计+jsp

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 需要源码或者定制看文章最下面或看我的主页 目 录 目 录 III 1 绪论 1 1.1 研究背景 1 1.2 目的和意义 1 1.3 论文结构安排 2 2 相关技术 3 2.1 SSM框架介绍 3 2.2 B/S结构介绍 3 …

算法提高 图形输出

时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 512MB&#xff0c;其他语言 1024MB 难度&#xff1a;困难 分数&#xff1a;100 OI排行榜得分&#xff1a;14(0.1*分数2*难度) 描述 编写一程序&#xff0c;在屏幕上输出如下内容&#xff1…

[程序设计]—代理模式

[程序设计]—代理模式&#x1f473; 本文章记录学习于——52.面向切面&#xff1a;AOP-场景模拟_哔哩哔哩_bilibili 最近闲来无事&#xff0c;在学习Spring的源码&#xff1a; 后面慢慢更新源码系列blog&#xff0c;希望多多关注&#x1f64f;&#x1f64f; 目前已经总结的b…

ue5玩家角色添加武器。切换武器位置,手上武器放到背上。演示一下人体插槽和武器的连接。仅仅演示,实际项目不是这么用的

把第一人称资源包导进来 这就是我们枪的骨骼网格体 我们找到这个骨骼 右手添加插槽 取个名字 因为武器上也有动画&#xff0c;所有武器单独写个蓝图类 新建一个蓝图类 BP_Weapon 把枪的蓝图拖到人的静态网格体下&#xff0c;成为一个部分 选中BP_Weapon的父类套接字…

如何选择适合的证件照制作软件,让您的照片制作更轻松

在当今数字化的时代&#xff0c;制作证件照不再需要专门前往照相馆。选择一款合适的证件照制作软件&#xff0c;您可以在家中轻松完成标准证件照的拍摄与制作。然而&#xff0c;面对市面上琳琅满目的软件&#xff0c;找到最适合您需求的软件并不简单。本文将为您详细介绍选择证…

数据挖掘实训:天气数据分析与机器学习模型构建

随着气候变化对各行各业的影响日益加剧&#xff0c;精准的天气预测已经变得尤为重要。降雨预测在日常生活中尤其关键&#xff0c;例如农业、交通和灾害预警等领域。本文将通过机器学习方法&#xff0c;利用历史天气数据预测明天是否会下雨&#xff0c;具体内容包括数据预处理、…

车载音频开发(二):对音频数据作音量调节

通过前一个章节打下的基础车载音频开发&#xff08;一&#xff09;&#xff1a;从看懂wav开始https://blog.csdn.net/Hellomino_/article/details/140873133?fromshareblogdetail&sharetypeblogdetail&sharerId140873133&sharereferPC&sharesourceHellomino_&…

Apache XMLBeans 一个强大的 XML 数据处理框架

Apache XMLBeans 是一个用于处理 XML 数据的 Java 框架&#xff0c;它提供了一种方式将 XML Schema (XSD) 映射到 Java 类&#xff0c;从而使得开发者可以通过强类型化的 Java 对象来访问和操作 XML 文档。下面将以一个简单的案例说明如何使用 Apache XMLBeans 来解析、生成和验…

74 mysql having 的实现

前言 这里 我们主要是 看一下 having 的相关实现 having 经常是配合 group by 这边进行使用, 进行一个基于 group by 之后的结果的一个, 条件限定 我们这里 以最简单的 group by having 来进行调试, 他会分为 两个阶段, 一个阶段是 group by 之后的结果输出到临时表, 另外…

PyCharm+RobotFramework框架实现UDS自动化测试——(一)python-can 库的安装与环境配置

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者&#xff0c;时光不负有心人。 文章目录 1. 概述2.安装 python-can 库—基于pycharm在对应的工程下3. 在任意盘中安装环境4. 导入 can 模块语法5. 配置 CAN 接口6.CANoe设备连接语法 1. 概述 本专栏主…

Java Spring Boot实现基于URL + IP访问频率限制

点击下载《Java Spring Boot实现基于URL IP访问频率限制(源代码)》 1. 引言 在现代 Web 应用中&#xff0c;接口被恶意刷新或暴力请求是一种常见的攻击手段。为了保护系统资源&#xff0c;防止服务器过载或服务不可用&#xff0c;需要对接口的访问频率进行限制。本文将介绍如…