【Lua】ToLua逻辑热更新

news2025/1/10 3:05:19

1 前言

        Lua基础语法 中系统介绍了 Lua 的语法体系,本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新。

        逻辑热更新是指:在保持程序正常运行的情况下,在后台修改代码逻辑,修改完成并推送到运行主机上,主机无缝接入更新后的代码逻辑。

        Unity3D 中,基于 Lua 的逻辑热更新方案主要有 ToLua、XLua、uLua、sLua,本文将介绍 ToLua 逻辑热更新方案。

        1)ToLua 插件下载

  • github:https://github.com/topameng/tolua
  • gitcode:https://gitcode.net/mirrors/topameng/tolua

        2)ToLua 插件导入

        将插件的 Assets 目录下的所有文件拷贝到项目的 Assets 目录下,如下:

        3) 生成 Wrap 文件

        导入插件后,菜单栏会多一个 Lua 窗口,点击 Generate All 会生成一些 Wrap 文件,生成路径见【Assets\Source\Generate】,这些 Wrap 文件是 Unity3D 与 Lua 沟通的桥梁。每次生成文件时,建议先点击下 Clear wrap files,再点击 Generate All。

        4)配置 wrap 文件

        用户如果想添加新的 wrap 配置,可以在【Assets\Editor\CustomSettings.cs】文件里添加,并且需要在菜单栏里重新点击 Generate All,在【Assets\Source\Generate】中查看是否生成了相应的文件。

        5)官方Demo

        本文基于官方 Demo 讲解 ToLua 的使用。

2 ToLua 应用

2.1 Unity3D 中执行 Lua 代码串

        HelloWorld.cs

using UnityEngine;
using LuaInterface;

public class HelloWorld : MonoBehaviour {
    void Awake()
    {
        LuaState lua = new LuaState();
        lua.Start();
        string luaStr = @"print('Hello World')";
        lua.DoString(luaStr, "HelloWorld.cs");
        lua.CheckTop();
        lua.Dispose();
        lua = null;
    }
}

        运行如下:

2.2 Unity3D 中调用 Lua 文件

        ScriptFromFile.cs

using UnityEngine;
using LuaInterface;

public class ScriptFromFile : MonoBehaviour {
    private LuaState lua = null;

    private void Start() {
        lua = new LuaState();
        lua.Start();
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\02");
        lua.DoFile("LuaScript.lua");
        lua.CheckTop();
    }

    private void OnApplicationQuit() {
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

print("Load lua script")

2.3 Unity3D 中调用 Lua 函数

        CallLuaFunction.cs

using UnityEngine;
using LuaInterface;
using System;

public class CallLuaFunction : MonoBehaviour {
    private LuaState lua = null;
    private LuaFunction luaFunc = null;

    private void Start() {
        lua = new LuaState();
        lua.Start();
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\03");
        lua.DoFile("LuaScript.lua");
        GetFunc();
        print("Invoke1: " + Invoke1());
        print("Invoke2: " + Invoke2());
        print("PCall: " + PCall());
        print("Delegate: " + Delegate());
        lua.CheckTop();
    }

    private void GetFunc() {
        //luaFunc = lua["luaFunc"] as LuaFunction; // 方式一
        luaFunc = lua.GetFunction("luaFunc"); // 方式二
    }

    private string Invoke1() { // 方法一
        string res = luaFunc.Invoke<int, string>(123456);
        return res;
    }

    private string Invoke2() { // 方法二
        string res = lua.Invoke<int, string>("luaFunc", 123456, true);
        return res;
    }

    private string PCall() { // 方法三
        luaFunc.BeginPCall();
        luaFunc.Push(123456);
        luaFunc.PCall();
        string res = luaFunc.CheckString();
        luaFunc.EndPCall();
        return res;
    }

    // 需要在CustomSettings.cs的customDelegateList里面添加"_DT(typeof(System.Func<int, string>))", 并且重新点击菜单窗口的Lua->Generate All
    private string Delegate() { // 方法四
        DelegateFactory.Init();
        Func<int, string> func = luaFunc.ToDelegate<Func<int, string>>();
        string res = func(123456);
        return res;
    }

    private void Call() { // 方法五(lua中无返回值的函数可以调用此方法)
        luaFunc.Call(123456);
    }

    private void OnApplicationQuit() { // 退出应用回调
        if (luaFunc != null) {
            luaFunc.Dispose();
            luaFunc = null;
        }
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

function luaFunc(num)                        
	return "num=" .. num
end

        打印如下: 

2.4 Unity3D 中调用 Lua 变量

        AccessLuaVar.cs

using UnityEngine;
using LuaInterface;

public class AccessLuaVar : MonoBehaviour {
    private LuaState lua = null;

    private void Start() {
        lua = new LuaState();
        lua.Start();
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\04");
        InitValue();
        AccessTab();
        lua.CheckTop();
    }

    private void InitValue() { // 在执行lua脚本前, 给lua脚本中变量赋值
        lua["var"] = 5;
        lua.DoFile("LuaScript.lua");
    }

    private void AccessTab() { // 访问tab
        LuaTable tab = lua.GetTable("tab"); // 获取table
        object[] list = tab.ToArray(); // 遍历table元素
        for (int i = 0; i < list.Length; i++)
        {
            print("tab[" + i + "]=" + list[i]);
        }
        print("a=" + tab["a"]);
        tab["a"] = "yyy";
        print("a=" + tab["a"]);
        LuaTable map = (LuaTable) tab["map"];
        print("name=" + map["name"] + ", age=" + map["age"]);
        map.Dispose();
        tab.Dispose();
    }

    private void OnApplicationQuit() { // 退出应用回调
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

print('var='..var)

tab = {1, 2, 3, a = "xxx"}
tab.map = {name = "zhangsan", age = 23}

        打印如下:

2.5 Lua 中使用协程

        1)方法一

        TestCoroutine.cs

using UnityEngine;
using LuaInterface;

//方法一和方法二展示的两套协同系统勿交叉使用,此为推荐方案
public class TestCoroutine : MonoBehaviour {
    private LuaState lua = null;
    private LuaLooper looper = null;
    private LuaFunction func = null;

	private void Awake() {
        lua  = new LuaState();
        lua.Start();
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\05");
        LuaBinder.Bind(lua);       
        looper = gameObject.AddComponent<LuaLooper>();
        looper.luaState = lua;
        lua.DoFile("LuaScript.lua");
        func = lua.GetFunction("TestCortinue");
        func.Call();
    }

    private void OnApplicationQuit() {
        if (func != null) {
            func.Dispose();
            func = null;
        }
        looper.Destroy();
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

function CortinueFunc()
	local www = UnityEngine.WWW("http://www.baidu.com")
	coroutine.www(www)
	local str = tolua.tolstring(www.bytes)
	print(str:sub(1, 128))
	print("current frameCount: "..Time.frameCount)
	coroutine.step() --挂起协程, 下一帧继续执行
	print("yield frameCount: "..Time.frameCount)
end

function TestCortinue()	
    coroutine.start(CortinueFunc)
end

        打印如下:

         2)方法二

        TestCoroutine.cs

using UnityEngine;
using LuaInterface;

//方法一和方法二展示的两套协同系统勿交叉使用,类unity原生,大量使用效率低
public class TestCoroutine : LuaClient {

    protected override void OnLoadFinished() {
        base.OnLoadFinished();
        luaState.AddSearchPath(Application.dataPath + "\\Scenes\\05");
        luaState.DoFile("LuaScript.lua");
        LuaFunction func = luaState.GetFunction("TestCortinue");
        func.Call();
        func.Dispose();
    }

    private new void OnApplicationQuit() {
        base.OnApplicationQuit();
    }
}

        说明: LuaClient 继承 MonoBehaviour。

        LuaScript.lua

function CortinueFunc()
    WaitForSeconds(1)
    print('WaitForSeconds end time: '.. UnityEngine.Time.time)
    WaitForFixedUpdate()
    print('WaitForFixedUpdate end frameCount: '..UnityEngine.Time.frameCount)
    WaitForEndOfFrame()
    print('WaitForEndOfFrame end frameCount: '..UnityEngine.Time.frameCount)
    Yield(null)
    print('yield null end frameCount: '..UnityEngine.Time.frameCount)
    Yield(0)
    print('yield(0) end frameCime: '..UnityEngine.Time.frameCount)
    local www = UnityEngine.WWW('http://www.baidu.com')
    Yield(www)
    print('yield(www) end time: '.. UnityEngine.Time.time)
    local str = tolua.tolstring(www.bytes)
    print(str:sub(1, 128))
end

function TestCortinue()
    StartCoroutine(CortinueFunc)
end

        打印如下:

2.6 Unity3D 给 Lua 传递数组

        TestArray.cs

using UnityEngine;
using LuaInterface;

public class TestArray : MonoBehaviour {
    private LuaState lua = null;
    private LuaFunction func = null;

	private void Start() {
        lua  = new LuaState();
        lua.Start();
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\06");
        lua.DoFile("LuaScript.lua");
        int[] array = {1, 2, 3};
        func = lua.GetFunction("TestArray");
        Call(array);
        //LazyCall(array);
        lua.CheckTop();
    }

    private void Call(int[] array) { // 方式一
        func.BeginPCall();
        func.Push(array);
        func.PCall();
        double arg1 = func.CheckNumber();
        string arg2 = func.CheckString();
        bool arg3 = func.CheckBoolean();
        func.EndPCall();
        print("arg1: " + arg1 + ", arg2: " + arg2 + ", arg3: " + arg3);
    }

    private void LazyCall(int[] array) { // 方式二
        object[] objs = func.LazyCall((object)array);
        if (objs != null) {
            print("objs[0]: " + objs[0] + ", objs[1]: " + objs[1] + ", objs[2]: " + objs[2]);
        }
    }

    private void OnApplicationQuit() {
        if (func != null) {
            func.Dispose();
            func = null;
        }
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

function TestArray(array)
    local len = array.Length
    --通过下标遍历数组
    for i = 0, len - 1 do
        print('Array: '..tostring(array[i]))
    end
    --通过迭代器遍历数组
    local iter = array:GetEnumerator()
    while iter:MoveNext() do
        print('iter: '..iter.Current)
    end
    --通过表格遍历数组
    local t = array:ToTable()
    for i = 1, #t do
        print('table: '.. tostring(t[i]))
    end
    --二分查找数组元素, 返回元素下标, 若数组中不存在该元素, 返回负数
    local pos = array:BinarySearch(3)
    print('array BinarySearch, pos='..pos..', value='..array[pos])
    --查找数组元素下标
    pos = array:IndexOf(4)
    print('array indexof, pos='..pos)
    --返回多值
    return 1, '123', true
end

2.7 Unity3D 给 Lua 传递字典

        TestDictionary.cs

using System.Collections.Generic;
using UnityEngine;
using LuaInterface;

public sealed class Account {
    public int id;
    public string name;
    public int sex;

    public Account(int id, string name, int sex) {
        this.id = id;
        this.name = name;
        this.sex = sex;
    }
}

public class TestDictionary : MonoBehaviour {
    private LuaState lua = null;
    private LuaFunction func = null;
    private Dictionary<int, Account> map = new Dictionary<int, Account>();

	private void Awake() {
        InitDirec();
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\07");
        lua.DoFile("LuaScript.lua");
        func = lua.GetFunction("TestDict");
        Call();
    }

    private void Call() {
        func.BeginPCall();
        func.Push(map);
        func.PCall();
        func.EndPCall();
    }

    private void InitDirec() {
        map.Add(1, new Account(1, "张三", 0));
        map.Add(2, new Account(2, "李四", 1));
        map.Add(3, new Account(3, "王五", 0));
    }

    private void OnApplicationQuit() {
        if (func != null) {
            func.Dispose();
            func = null;
        }
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

function TestDict(map)
    --遍历map的所有value元素
    local iter = map:GetEnumerator()
    while iter:MoveNext() do
        local v = iter.Current.Value
        print('id: '..v.id ..', name: '..v.name..', sex: '..v.sex)
    end
    --遍历map的key集
    local keys = map.Keys
    iter = keys:GetEnumerator()
    print('------------print dictionary keys---------------')
    while iter:MoveNext() do
        print(iter.Current)
    end
    --遍历map的value集
    local values = map.Values
    iter = values:GetEnumerator()
    print('------------print dictionary values---------------')
    while iter:MoveNext() do
        print(iter.Current.name)
    end
    --根据key查找value元素
    local flag, account = map:TryGetValue(1, nil)
    if flag then
        print('TryGetValue result ok: '..account.name)
    end
    print('kick '..map[2].name)
    --移除元素, 并打印剩余的value
    map:Remove(2)
    iter = map:GetEnumerator()
    while iter:MoveNext() do
        local v = iter.Current.Value
        print('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)
    end
end

        补充:CustomSettings.cs 里需要配置 Account 如下:

    //在这里添加你要导出注册到lua的类型列表
    public static BindType[] customTypeList = {                
        //------------------------为例子导出--------------------------------
        _GT(typeof(Account)),
        _GT(typeof(Dictionary<int, Account>)).SetLibName("AccountMap"),
        _GT(typeof(KeyValuePair<int, Account>)),
        _GT(typeof(Dictionary<int, Account>.KeyCollection)),
        _GT(typeof(Dictionary<int, Account>.ValueCollection)),
        //-------------------------------------------------------------------
        ...
    }

        配置后,需要点击 Generate All 生成相关 Wrap 文件,如果出现以下报错:

LuaException: LuaScript.lua:12: field or property MoveNext does not exist

        修改 System_Collections_Generic_Dictionary_int_Account_KeyCollectionWrap.cs 和 System_Collections_Generic_Dictionary_int_Account_ValueCollectionWrap.cs 文件,将 ToLua.PushValue(L, o) 替换为 ToLua.Push(L, o),如下:

//ToLua.PushValue(L, o);
ToLua.Push(L, o);

2.8 Unity3D 给 Lua 传递列表

        TestList.cs

using UnityEngine;
using LuaInterface;
using System.Collections.Generic;

public class TestList : MonoBehaviour {
    private LuaState lua = null;

    private void Awake() {
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\08");
        lua.DoFile("LuaScript.lua");
        DelegateFactory.Init();
        List<string> list = GetList();
        Call(list);
    }

    private void Call(List<string> list) {
        LuaFunction func = lua.GetFunction("TestList");
        func.BeginPCall();
        func.Push(list);
        func.PCall();
        func.EndPCall();
        func.Dispose();
        func = null;
    }

    private List<string> GetList() {
        List<string> list = new List<string>();
        list.Add("zhang");
        list.Add("li");
        list.Add("wang");
        return list;
    }

    private void OnApplicationQuit() {
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua


function printList(list)
    str = ""
    for i = 0, list.Count - 1 do
        str = str..(list[i])..", "
    end
    print(str)
end

function TestList(list)
    printList(list) --zhang, li, wang, 
    list:Add("chen")
    printList(list) --zhang, li, wang, chen, 
    list:Sort()
    printList(list) --chen, li, wang, zhang, 
    print("index="..list:IndexOf("wang")) --index=2
    list:Remove("li")
    printList(list) --chen, wang, zhang, 
end

2.9 Lua 中创建 GameObject 并获取和添加组件

        TestGameObject.cs

using UnityEngine;
using LuaInterface;

public class TestGameObject : MonoBehaviour {
    private LuaState lua = null;

	private void Awake() {
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\09");
        lua.DoFile("LuaScript.lua");
    }

    private void OnApplicationQuit() {
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua

local GameObject = UnityEngine.GameObject
local PrimitiveType = UnityEngine.PrimitiveType --需要在CustomSettings.cs里添加"_GT(typeof(PrimitiveType))"
local MeshRenderer = UnityEngine.MeshRenderer
local Color = UnityEngine.Color
local Rigidbody = UnityEngine.Rigidbody

go = GameObject.CreatePrimitive(PrimitiveType.Cube)
go:GetComponent("MeshRenderer").sharedMaterial.color = Color.red
rigidbody = go:AddComponent(typeof(Rigidbody))
rigidbody.mass = 1000

3 Lua Hook Unity3D 生命周期方法

        TestLife.cs

using UnityEngine;
using LuaInterface;
using System.Collections.Generic;

public class TestLife : MonoBehaviour {
    private LuaState lua = null;
    private Dictionary<string, LuaFunction> func;

    private void Awake() {
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        lua.AddSearchPath(Application.dataPath + "\\Scenes\\10");
        lua.DoFile("LuaScript.lua");
        GetFunc();
        CallFunc("awake");
    }

    private void OnEnable() {
        CallFunc("onEnable");
    }

    private void Start() {
        CallFunc("start");
    }

    private void Update() {
        CallFunc("update");
    }

    private void OnDisable() {
        CallFunc("onDisable");
    }

    private void OnDestroy() {
        CallFunc("onDestroy");
    }

    private void GetFunc() {
        func = new Dictionary<string, LuaFunction>();
        AddFunc("awake");
        AddFunc("onEnable");
        AddFunc("start");
        AddFunc("update");
        AddFunc("onDisable");
        AddFunc("onDestroy");
    }

    private void AddFunc(string funcName) {
        LuaFunction fun = lua.GetFunction(funcName);
        if (fun != null) {
            func.Add(funcName, fun);
        }
    }

    private void CallFunc(string funcName) {
        if (func.ContainsKey(funcName)) {
            LuaFunction fun = func[funcName];
            fun.Call();
        }
    }

    private void OnApplicationQuit() {
        foreach(var fun in func.Values)
        {
            fun.Dispose();
        }
        func.Clear();
        func = null;
        lua.Dispose();
        lua = null;
    }
}

        LuaScript.lua


function awake()
    print("awake")
end

function onEnable()
    print("onEnable")
end

function start()
    print("start")
end

function update()
    print("update")
end

function onDisable()
    print("onDisable")
end

function onDestroy()
    print("onDestroy")
end

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS原子化服务最新概念、呈现形式与触发方式

一、HarmonyOS原子化服务最新概念 总体介绍&#xff1a; 原子化服务&#xff08;Ability&#xff09;是由HarmonyOS服务开放平台接入的一种技能&#xff0c;为华为智慧服务、智慧搜索、服务直达、智慧语音以及智慧视觉等流量入口提供服务技能。原子化服务以轻量化的呈现形式&…

高级网络应用复习——TCP与UDP,ACL列表, 防火墙,NAT复习与实验(带命令)

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.知识点总结 1.传输层的协议 &#xff08;1&#xff09;TCP 和…

「实操」结合图数据库、图算法、机器学习、GNN 实现一个推荐系统

本文是一个基于 NebulaGraph 上图算法、图数据库、机器学习、GNN 的推荐系统方法综述&#xff0c;大部分介绍的方法提供了 Playground 供大家学习。 基本概念 推荐系统诞生的初衷是解决互联网时代才面临的信息量过载问题&#xff0c;从最初的 Amazon 图书推荐、商品推荐&…

TCP的三次握手四次挥手详解

想要了解TCP的三次握手和四次挥手&#xff0c;首先要了解TCP的头部结构 TCP的头部结构如下 16位源端口号&#xff1a; 客户端地址信息16位目标端口号&#xff1a; 服务端地址信息32位序列号&#xff1a; 请求报文端数据流子节开始的位置&#xff08;比如位1024&#xff5e;2…

【20天快速掌握Python】day15-网络编程

1.网络通信的概念 简单来说&#xff0c;网络是用物理链路将各个孤立的工作站或主机相连在一起&#xff0c;组成数据链路&#xff0c;从而达到资源共享和通信的目的。 使用网络的目的&#xff0c;就是为了联通多方然后进行通信&#xff0c;即把数据从一方传递给另外一方。 前…

以分页场景谈MVC设计模式

一 、需求场景 需要实现一个分页组件&#xff0c; 可以方便的进行分页操作。 二、分析需求 从分页需求出发&#xff0c;分析潜在的元素&#xff0c; 虽然只包含一个大的分页功能&#xff0c;但是潜在的元素 包含&#xff1a;上一页 下一页 首页 尾页 当前页 等等。 为什么包含…

【Oauth2】SpringBoot整合Oauth2实现认证授权

SpringBoot整合Oauth2实现认证授权 应用场景 OAuth2.0 协议的使用场景主要为&#xff1a;第三方登录和开放接口的调用 第三方登录就是使用微信等第三方的方式来登录一个应用或者网站&#xff0c;比如用微信账号登录gitee。 而开发接口的调用&#xff0c;则比如说微信、美团…

TP5的消息队列

1.首先查看项目中是否已经有think-queue目录&#xff1a;/vendor/topthink/ 微信截图_20200909142126.png 如果没有&#xff0c;则用composer安装&#xff08;安装composer参考&#xff1a;http://www.runoob.com/w3cnote/composer-install-and-usage.html &#xff09;&#x…

Qt文档阅读笔记-Qt, QML, Widgets…What Is The Difference?

Qt, QML, Widgets…What Is The Difference? 本节主要介绍了开发Qt程序最关机的几个组建。 Qt是使用C和一些C的框架设计编写出来的。 Qt Qt是一个开源的框架。 Qt作为一个框架&#xff0c;包含了许多组件&#xff0c;这些组建又在指定的模块中&#xff0c;Qt基础组件在&…

干货| 小游戏赛道变现指南

随着羊了个羊等小游戏的爆火&#xff0c;不少人发现了小游戏赛道的巨大潜力&#xff0c;也想要在此赛道有所尝试。但是很多游戏赛道的新人对于小游戏变现问题存有疑问&#xff0c;今天就来跟大家分享一下小程序游戏赛道的变现途径&#xff01; 近期FinClip 官方正在举行小游戏…

全志 芯片 Linux MIPI CSI摄像头接口开发指南 VIN DVP CSI MIPI V4l2

1 前言 1.1 文档简介 介绍 VIN&#xff08;video input&#xff09;驱动配置&#xff0c;API 接口和上层使用方法。 1.2 目标读者 camera 驱动开发、维护人员和应用开发人员。 1.3 适用范围 ​ 表 1-1: 适用产品列表 内核版本驱动文件Linux-4.9drivers/media/platform/s…

nacos源码分析-服务注册(服务端)

安装Nacos源码 上一篇文章我们了解了《Nacos服务注册》客户端源码&#xff0c;本篇文章我们来看一下服务注册Nacos服务端的源码执行情况。首先需要下载Nacos源码&#xff0c; https://github.com/alibaba/nacos/releases/tag/1.4.3 &#xff0c; 解压之后使用IDEA工具导入即可…

Web3中文|为什么去中心化存储对NFT元数据很重要

图中文字&#xff1a;哦&#xff0c;看&#xff0c;FTX用Web2 API托管了所有在其平台上铸造的NFT&#xff0c;现在所有这些NFT的元数据都被破坏了&#xff0c;并且链接到了一个重组的网站。 这本不应该发生。但对于任何不考虑元数据和如何存储元数据的NFT项目来说&#xff0c;…

docker(5):Dockerfile

目录Dockerfile介绍Dockerfile常用指令案例&#xff1a;构建tomcat镜像Dockerfile介绍 Dockerfile 是一个用来构建镜像的文本文件&#xff0c;文本内容包含了一条条构建镜像所需的指令和说明&#xff0c;每条指令都会创建一个新的镜像层并对镜像进行提交。 Dockerfile 一般分…

【Django】第一课 基于Django图书借阅管理网站平台

概念 django服务器开发框架是一款基于Python编程语言用于web服务器开发的框架&#xff0c;采用的是MTV架构模式进行分层架构。 项目搭建 打开pycharm开发软件&#xff0c;打开开发软件的内置dos窗口操作命令行 在这里指定项目存放的磁盘路径&#xff0c;并使用创建django项目…

编辑器:保存格式化修复配置

规范化条目 制表符长度&#xff1a;2&#xff0c;缩进模式&#xff1a;2个空格&#xff0c;换行符&#xff1a;lf&#xff0c;末尾加分号&#xff0c;js单引号&#xff0c;冒号后一个空格&#xff0c;运算符前后一个空格&#xff0c;大括号&#xff08;有内容的&#xff09;首…

项目实战之旅游网(八)后台产品管理(下)

目录 一.上传产品图片 二.修改产品 三.上下架产品 一.上传产品图片 在新增产品时&#xff0c;我们还需要上传产品图片。我们采用异步上传的方法进行图片上传。 1.在conmmon_ resources.html 中引入jqueryform.js 2.修改product_ add.html 页面 点击保存 &#xff0c;自动…

【Spring【IOC】】——18、自定义组件中如何注入Spring底层的组件?

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

LeetCode283.移动0

思路1 分析 在i位置遇到0&#xff0c;把后面的元素向前移动覆盖&#xff0c;然后把最后一个位置赋值为0即可 注意问题&#xff1a; 可能 i 一个位置 移动一次之后还是0&#xff0c;需要循环 有可能 i 位置的0 是因为 已经所有的0都到后面了 ​ 所以需要用count记录0的个数&am…

2022年区块链安全领域8成以上损失集中在DeFi和跨链桥

近期&#xff0c;欧科云链研究院上线《2022年全球区块链生态安全态势报告》&#xff0c;报告指出2022年区块链安全领域8成以上损失集中在DeFi和跨链桥&#xff0c;钓鱼攻击是最常见攻击手法。主要结论 2022年前11个月&#xff0c;OKLink共监测到区块链生态相关安全事件275起&a…