行为树详解(6)——黑板模式
【动作节点数据共享】
行为树中需要的参数可以来自游戏中的各个模块,如果仅需从多个模块获取少量参数,那么可以直接在代码中调用其他模块的单例继而层层调用获取数据。
如果获取的参数量很大,从架构上看,我们需要通过加一个中间者去管理各个模块的参数获取调用,行为树从中间者获取数据即可。
换一种说法就是要有共享数据的地方,通常会采用黑板模式。
综合来说,存在以下情况:
- 多个不同的动作节点或条件节点需要获取或设置来自不同模块的属性
- 多个不同的动作节点或条件节点会获取或设置相同模块的同一属性
- 不同动作节点之间有通信,A动作节点生成的临时数据是B动作节点所需的数据
- 不同动作节点存在大量重复计算,例如距离计算
- 多个动作节点会共用临时存在的多个数据
针对这些情况,我们可以通过键值对的形式实现黑板模式
需要注意的是,这些黑板不属于节点,考虑到不同行为树也会共享数据,因此也不一定属于黑板。需要有一个黑板的管理者来做数据管理。
黑板模式的数据管理本质还是通过键值对的方式,为处理不同的情况,我们需要对每种情况提供不同的Key。这和MVC中的数据管理并无本质区别。
和节点参数配置不同的是,这里是要程序做控制的,而且不确定性更大,无法做明确的规定。
在这种情况下,我们需要对每个数据做单独得ID定义,每个数据有各自的获取设置方法,通过ID映射。
根据行为树ID,节点ID,数据ID,方法ID可以实现不同的数据获取,程序只需实现方法ID即可。
在黑板中,我们需要根据这些参数生成唯一的Key,这里自然而然的就会需要对参数做封装,用泛型,用对象池。
同样的,我们需要有这些参数对应的结果,考虑数据类型差异,结果有效性等,自然也需要做封装。
【动作节点的实现位置】
在整个游戏中,与角色相关的模块如下:
- 角色动画,基于状态机提供动作切换,提供最基础的接口
- 角色运动,包括基础移动(走、跑等),地形移动(蹲下、跳跃、攀爬等),寻路。会调用角色动画提供的接口
- 角色交互:
- 与物体的交互(拾取、推开、握住、抓住、攀绕、踢开等等)。会调用角色动画或角色运动提供的接口,前者为主
- 与角色的交互(主要是打击、少量握手、拥抱等)。会调用角色动画或角色运动提供的接口,前者为主
- 角色技能:技能、Buff、伤害计算、效果表现。会调用角色动画或角色运动或角色交互提供的接口,前者为主
- 角色属性:记录角色的各类状态
- 角色行为:这里就是角色AI,会调用角色动画或角色运动或角色交互或角色技能或角色属性提供的接口
因此,在实现动作节点时,属于其他模块的直接调用其他模块的接口或在其他模块内实现,属于角色行为的在动作节点内实现。
例如,就技能而言,对角色技能来说不同角色的技能各有差异,要做不同的实现;但对角色AI而言,只有普攻、1技能、2技能、大招等
【代码实现】
数据配置
[Serializable]public class DataConfig{public int dataId;public int setMethodId;public int getMethodId;public DataLife dataLife;public bool multi;//同一类型参数,参数值不同,结果不同public bool praseType;//如果解析类型,做自动化生成public bool cache;//是否做数据缓存}public enum DataLife{Persistent,//永久性数据Conditional,//条件性数据,满足某条件出现,不满足消失FixedTime,//固定时间内有效的数据FixedFrame,//固定帧数内有效的数据}[CreateAssetMenu(fileName = "BlackBoard_Data_Config", menuName = "BT/BlackBoardDataConfig")]public class BlackBoardDataConfig:ScriptableObject{public List<DataConfig> dataConfigs = new List<DataConfig>();}
数据请求
public class BBDataRequest{public int btId;public int nodeId;public int dataId;public virtual void Release() { }}public class BBDataRequest<Parmas> : BBDataRequest{private static ObjectPool<BBDataRequest<Parmas>> Pool = new ObjectPool<BBDataRequest<Parmas>>(GenBBDataRequest);private static BBDataRequest<Parmas> GenBBDataRequest(){return new BBDataRequest<Parmas>();}public static BBDataRequest<Parmas> GetBBDataRequest(){return Pool.Get();}public static void Release(BBDataRequest<Parmas> data){data.Reset();Pool.Release(data);}public virtual int TryAddParams(Parmas data){return -1;}public virtual int TryAddObject(object data){return -1;}public virtual Parmas GetParmas(int index){return default(Parmas);}public virtual object GetObject(int index){return null;}public override void Release(){Release(this);}public virtual void Reset(){}}public class BBDataRequestSingle<Parmas> : BBDataRequest<Parmas>{public Parmas reqParamsNoBoxing;public object reqParams;public override int TryAddParams(Parmas data){reqParamsNoBoxing = data;return -1;}public override int TryAddObject(object data){reqParams = data;return -1;}public override Parmas GetParmas(int index){return reqParamsNoBoxing;}public override object GetObject(int index){return reqParams;}public override void Reset(){reqParamsNoBoxing = default(Parmas);reqParams = default(object);}}public class BBDataRequestMulti<Parmas>:BBDataRequest<Parmas>{public Dictionary<Parmas,int> paramsNoBoxingIndex = new Dictionary<Parmas,int>();public Dictionary<object,int> paramsIndex = new Dictionary<object,int>();private Dictionary<int, Parmas> index2ParamsNoBoxing = new Dictionary<int, Parmas>();private Dictionary<int,object> index2Params = new Dictionary<int,object>();public override int TryAddParams(Parmas data){if(!paramsNoBoxingIndex.TryGetValue(data,out int res)){res = paramsNoBoxingIndex.Count;paramsNoBoxingIndex[data] = res;}index2ParamsNoBoxing[res] = data;return res;}public override int TryAddObject(object data){if(!paramsIndex.TryGetValue(data,out int res)){res = paramsIndex.Count;paramsIndex[data] = res;}index2Params[res] = data;return res;}public override object GetObject(int index){return index2Params[index];}public override Parmas GetParmas(int index){return index2ParamsNoBoxing[index];}public override void Reset(){paramsIndex.Clear();index2Params.Clear();index2ParamsNoBoxing.Clear();paramsNoBoxingIndex.Clear();}}
数据结果
public class BBDataResult<Result> : IBBDataResult{public int dataId { get; set; }public DataConfig config { get; set; }public BBDataRequest request { get; set; } public float lifeTime;public float curTime;public int curFrame;public bool Valid(){switch(config.dataLife){case DataLife.FixedTime:case DataLife.FixedFrame:return curTime > lifeTime;case DataLife.Conditional:case DataLife.Persistent: return true;}return true; }public virtual bool Getted(int frameCount, int index){return false;}public virtual Result GetCurResult(int index){return default(Result);}public virtual void SetGetResult(Result value, int index){}public virtual void SetCurResult(Result value,int index){}public virtual void Tick(float deltaTime){curFrame = Time.frameCount;if (config.dataLife == DataLife.FixedTime){curTime += deltaTime;}if(config.dataLife == DataLife.FixedFrame){curTime += 1;}}public virtual void Reset(){curTime = 0;curFrame = 0;}private static ObjectPool<BBDataResult<Result>> Pool = new ObjectPool<BBDataResult<Result>>(GenBBDataResult);private static BBDataResult<Result> GenBBDataResult(){return new BBDataResult<Result>();}public static BBDataResult<Result> GetBBDataResult(){return Pool.Get();}public static void Release(BBDataResult<Result> bbDataResult){bbDataResult.Reset();Pool.Release(bbDataResult);}public void Release(){Release(this);}}public class BBDataResultSingle<Result>: BBDataResult<Result>{public Result result;public bool getted;public override void SetGetResult(Result value, int index){result = value;getted = true;}public override void SetCurResult(Result value,int index){result = value;getted = false;}public override bool Getted(int frameCount, int index){return getted && frameCount == curFrame;}public override void Tick(float deltaTime){base.Tick(deltaTime);getted = false;}public override void Reset(){getted = false;result = default(Result);}public override Result GetCurResult(int index){return result;}}public class BBDataResultMulti<Result>: BBDataResult<Result>{public Dictionary<int,Result> resultIndex = new Dictionary<int,Result>();public Dictionary<int, bool> getted = new Dictionary<int, bool>();public override void SetGetResult(Result value,int index){resultIndex[index] = value;getted[index] = true;}public override void SetCurResult(Result value, int index){resultIndex[index] = value;getted[index] = false;}public override bool Getted(int frameCount,int index){return getted[index] && frameCount == curFrame;}public override void Tick(float deltaTime){base.Tick(deltaTime);foreach (var item in getted.Keys){getted[item] = false;}}public override void Reset(){getted.Clear();resultIndex.Clear();}public override Result GetCurResult(int index){return resultIndex[index];}}
黑板类及其管理者
public class BlackBoardManager{private static BlackBoardManager instance;private BlackBoardManager() { }public static BlackBoardManager Instance{get{if (instance == null){instance = new BlackBoardManager();}return instance;}}public Dictionary<int, BlackBoard> id2BB = new Dictionary<int, BlackBoard>();private Dictionary<int,DataConfig> dataConfig = new Dictionary<int, DataConfig>();public void Init(){BlackBoard bb = new BlackBoard();bb.bbId = 1;id2BB[1] = bb;//load配置数据 }public void Tick(float deltaTime){foreach (var bb in id2BB.Values){bb.Tick(deltaTime);}}public BlackBoard CreateBlackBoard(bool common){if (common){return id2BB[1];}else{BlackBoard bb = new BlackBoard();bb.bbId = id2BB.Count + 1;id2BB[bb.bbId] = bb;return bb;}}public BlackBoard GetBlackBoard(int bbId){id2BB.TryGetValue(bbId, out var bb);return bb;}public DataConfig GetDataConfig(int id){return dataConfig[id];}public void RemoveBlackBoard(BlackBoard bb){id2BB.Remove(bb.bbId);}public void Clear(){foreach(var bb in id2BB.Values){bb.Clear();}id2BB.Clear();dataConfig.Clear();}}public class BlackBoard{public int bbId;public Dictionary<int, IBBDataResult> id2Result = new Dictionary<int, IBBDataResult>();public Dictionary<int,BBDataRequest> id2Request;//这里简单根据Id做划分,可以做更复杂的分类,以便于收集数据做数据分析或Debugprivate List<int> waitRemoveList = new List<int>();private List<BBDataRequest> reqHistory = new List<BBDataRequest>();//可以收集数据做分析public void Tick(float deltaTime)//Tick检查去掉无效数据{waitRemoveList.Clear();foreach (var item in id2Result){item.Value.Tick(deltaTime);if(!item.Value.Valid()){waitRemoveList.Add(item.Key);}}foreach (var item in waitRemoveList){RemoveData(item);}}public Result GetData<Params,Result>(int btId,int nodeId,int dataId, Params reqparams,out bool valid){var config = BlackBoardManager.Instance.GetDataConfig(dataId);//根据数据Id获取数据配置var request = GetBBDataRequest<Params>(btId,nodeId,dataId,reqparams,config.multi && config.cache, out int index);//根据参数获取请求,分为Single请求和Multi请求var result = GetBBDataResult<Result>(dataId, config, request);//获取请求对应的结果//一个数据Id只有一个对应的请求和结果valid = result.Valid();if(valid){if(config.praseType){BBDataMethod.DispatchMethoId<Params,Result>(result.config.getMethodId, bbId, dataId, index, true);//自动解析传入的参数和结果的类型,自动化生成代码,适用于简单的值类型}else{BBDataMethod.DispatchMethoId(result.config.getMethodId, bbId, dataId, index);//自定义处理数据类型}return ((BBDataResult<Result>)result).GetCurResult(index);//同一个数据Id,在获取时会传入不同的参数,在请求中,给参数生成Index,根据Index获取其对应的结果} return default(Result);}public Result GetData<Result>(int btId, int nodeId, int dataId, object reqparams, out bool valid){var config = BlackBoardManager.Instance.GetDataConfig(dataId);var request = GetBBDataRequest(btId, nodeId, dataId, reqparams, config.multi && config.cache, out int index);var result = GetBBDataResult<Result>(dataId, config, request);valid = result.Valid();if (valid){if (config.praseType){BBDataMethod.DispatchMethoId<object, Result>(result.config.getMethodId, bbId, dataId, index, true);}else{BBDataMethod.DispatchMethoId(result.config.getMethodId, bbId, dataId, index);}return ((BBDataResult<Result>)result).GetCurResult(index);}return default(Result);}public void SetData<Params, Value>(int btId, int nodeId, int dataId,Value value, Params reqparams = default){var config = BlackBoardManager.Instance.GetDataConfig(dataId);//根据数据Id获取数据配置var request = GetBBDataRequest<Params>(btId, nodeId, dataId, reqparams, config.multi && config.cache, out int index);//根据参数获取请求,分为Single请求和Multi请求var result = GetBBDataResult<Value>(dataId, config, request);//获取请求对应的结果if (!((BBDataResult<Value>)result).GetCurResult(index).Equals(value))//判断设置的值是否和当前的结果值相当,如果相等就不用再设置了{if (config.praseType){BBDataMethod.DispatchMethoId<Params, Result>(result.config.setMethodId, bbId, dataId, index, false);}else{BBDataMethod.DispatchMethoId(result.config.setMethodId, bbId, dataId, index);}} }public void SetData<Value>(int btId, int nodeId, int dataId, Value value,object reqparams = null){var config = BlackBoardManager.Instance.GetDataConfig(dataId);var request = GetBBDataRequest(btId, nodeId, dataId, reqparams, config.multi && config.cache, out int index);var result = GetBBDataResult(dataId, config, request);if (!((BBDataResult<Value>)result).GetCurResult(index).Equals(value)){if (config.praseType){BBDataMethod.DispatchMethoId<object, Result>(result.config.setMethodId, bbId, dataId, index, false);}else{BBDataMethod.DispatchMethoId(result.config.setMethodId, bbId, dataId, index);}}}public bool RemoveData(int dataId){int count = 0;if(id2Result.TryGetValue(dataId,out var result)){result.Release();id2Result.Remove(dataId);count++;}if(id2Request.TryGetValue(dataId,out var request)){request.Release();id2Request.Remove(dataId);count++;}return count == 2;}public BBDataRequest GetDataRequest(int dataId){id2Request.TryGetValue(dataId, out var result);return result;}public IBBDataResult GetDataResult(int dataId){id2Result.TryGetValue(dataId, out var result);return result;}public void Clear(){id2Request.Clear();id2Result.Clear();waitRemoveList.Clear();//SaveHistoryreqHistory.Clear();}private BBDataRequest GetBBDataRequest<T>(int btId, int nodeId, int dataId,T data,bool multi,out int index){if(!id2Request.TryGetValue(dataId,out var request)){request = multi ? BBDataRequestMulti<T>.GetBBDataRequest() : BBDataRequestSingle<T>.GetBBDataRequest();request.btId = btId;request.nodeId = nodeId;request.dataId = dataId;//reqHistory.Add(request);}var res = request as BBDataRequest<T>;index = res.TryAddParams(data);//将获取数据传入的参数封装在 BBDataRequest中return res;}private BBDataRequest GetBBDataRequest(int btId, int nodeId, int dataId, object data,bool multi,out int index){if (!id2Request.TryGetValue(dataId, out var request)){request = multi ? BBDataRequestMulti<object>.GetBBDataRequest() : BBDataRequestSingle<object>.GetBBDataRequest();request.btId = btId;request.nodeId = nodeId;request.dataId = dataId;//reqHistory.Add(request);}var res = request as BBDataRequest<object>;index = res.TryAddObject(data);return res;}private IBBDataResult GetBBDataResult<T>(int dataId,DataConfig config,BBDataRequest request){if (!id2Result.TryGetValue(dataId, out var result)){BBDataResult<T> res = (config.multi && config.cache) ? BBDataResultMulti<T>.GetBBDataResult() : BBDataResultSingle<T>.GetBBDataResult();if (!config.cache) res.SetCurResult(default(T), 0);result = res;result.dataId = dataId;result.config = config;}result.request = request;return result;}private IBBDataResult GetBBDataResult(int dataId, DataConfig config, BBDataRequest request){if (!id2Result.TryGetValue(dataId, out var result)){BBDataResult<object> res = (config.multi && config.cache) ? BBDataResultMulti<object>.GetBBDataResult() : BBDataResultSingle<object>.GetBBDataResult();if (!config.cache) res.SetCurResult(null, 0);result = res;result.dataId = dataId;result.config = config;}result.request = request;return result;}}
数据的GetSet方法实
public static class BBDataDefinition{//这里通过配置自动生成public const int Def_获取血量 = 11223344;public const int Def_设置血量 = 11223345;public const int Def_获取资源数量 = 121212123;public const int Def_设置资源数量 = 121212124;}public partial class BBDataMethod{//这里通过配置自动生成private static Dictionary<(Type, Type), Action<BBDataRequest,IBBDataResult,int,int,bool>> TypeToPraseAction = new Dictionary<(Type, Type), Action<BBDataRequest, IBBDataResult, int, int,bool>>(){[(typeof(void),typeof(int))] = PraseVoidAndInt,[(typeof(int), typeof(int))] = PraseIntAndInt,[(typeof(int), typeof(void))] = PraseIntAndVoid,};private static Dictionary<int, Func<int>> GetIntValue = new Dictionary<int, Func<int>>(){[BBDataDefinition.Def_获取资源数量] = GetResCount,};private static Dictionary<int, Action<int>> SetIntValue = new Dictionary<int, Action<int>>(){[BBDataDefinition.Def_设置资源数量] = SetResCount,};private static Dictionary<int, Func<int,int>> GetIntValueByInt = new Dictionary<int, Func<int,int>>(){};private static Dictionary<int, Action<int, int>> SetIntValueByInt = new Dictionary<int, Action<int, int>>(){};public static void DispatchMethoId<Params,Result>(int methodId,int bbId,int dataId,int index,bool get){var bb = BlackBoardManager.Instance.GetBlackBoard(bbId);var res = bb.GetDataResult(dataId);if (get && res != null && res.Getted(Time.frameCount, index)){return;}var req = bb.GetDataRequest(dataId);if (req != null && res != null){var typeReq = typeof(Params);var typeRes = typeof(Result);TypeToPraseAction.TryGetValue((typeReq, typeRes), out var action);if (action != null){action(req, res, methodId, index, get);}}}public static void DispatchMethoId(int methodId, int bbId, int dataId, int index){switch (methodId){case BBDataDefinition.Def_获取血量: GetRoleHp(bbId, dataId, index); break;case BBDataDefinition.Def_设置血量: SetRoleHp(bbId, dataId, index); break;}}private static void PraseVoidAndInt(BBDataRequest req, IBBDataResult res, int methodId, int index,bool get){if(get){int intValue = GetIntValue[methodId].Invoke();var intResult = res as BBDataResult<int>;intResult.SetGetResult(intValue, index);}}private static void PraseIntAndVoid(BBDataRequest req, IBBDataResult res, int methodId, int index, bool get){if (!get){var intResult = res as BBDataResult<int>;int intValue = intResult.GetCurResult(index);SetIntValue[methodId].Invoke(intValue);}}private static void PraseIntAndInt(BBDataRequest req, IBBDataResult res, int methodId, int index, bool get){if(get){var intReq = req as BBDataRequest<int>;int intParams = intReq.GetParmas(index);int intValue = GetIntValueByInt[methodId].Invoke(intParams);var intResult = res as BBDataResult<int>;intResult.SetGetResult(intValue, index);}else{var intReq = req as BBDataRequest<int>;int intParams = intReq.GetParmas(index);var intResult = res as BBDataResult<int>;int intValue = intResult.GetCurResult(index);SetIntValueByInt[methodId].Invoke(intParams, intValue);}}}public partial class BBDataMethod{public static void GetRoleHp(int bbId, int dataId,int index){var bb = BlackBoardManager.Instance.GetBlackBoard(bbId);//获取数据所在的BBvar res = bb.GetDataResult(dataId);//获取数据对应的结果if(res != null && res.Getted(Time.frameCount,index))//判断当前帧该数据是否已经获取过{return;}var req = bb.GetDataRequest(dataId);//获取数据对应的请求 if(req != null ){ var intReq = req as BBDataRequest<int>;int roleId = intReq.GetParmas(index);//获取请求的参数int hp = 100;//通过角色Id获取角色属性,属性系统固定时,这些类似的获取值的代码都可以通过自动化配置生成 var intResult = res as BBDataResult<int>;intResult.SetGetResult(hp,index);//设置获取的结果}}public static void SetRoleHp(int bbId, int dataId,int index){var bb = BlackBoardManager.Instance.GetBlackBoard(bbId);var res = bb.GetDataResult(dataId);var req = bb.GetDataRequest(dataId);if (req != null && res != null){var intReq = req as BBDataRequest<int>;int roleId = intReq.GetParmas(index);var intResult = res as BBDataResult<int>;int hp = intResult.GetCurResult(index); //调用接口设置角色血量}}public static int GetResCount() { return 100; }public static void SetResCount(int value) { }}
相关文章:
行为树详解(6)——黑板模式
【动作节点数据共享】 行为树中需要的参数可以来自游戏中的各个模块,如果仅需从多个模块获取少量参数,那么可以直接在代码中调用其他模块的单例继而层层调用获取数据。 如果获取的参数量很大,从架构上看,我们需要通过加一个中间…...
Vue.js与其他框架有哪些兼容性?
Vue.js的兼容性主要体现在几个方面,包括浏览器支持、运行环境适应性、与其他库和框架的集成能力等。以下是更详细的解释: 浏览器兼容性 现代浏览器:Vue.js广泛支持所有主流的现代浏览器,如Google Chrome, Firefox, Safari, Edge…...
Java 8 Stream 介绍
Java 8 Stream 介绍 1. 什么是Stream? Stream(流)是Java 8引入的全新概念,它是一个支持串行和并行聚合操作的元素序列。Stream API提供了一种声明式的方式来处理数据集合,可以让我们以一种类似SQL查询的方式处理数据…...
Java NIO、AIO分析
好的,下面将对Java中的**NIO(Non-blocking IO)和AIO(Asynchronous IO)**进行更深入的分析,重点探讨它们的特点和具体的应用场景。 一、Java NIO(Non-blocking IO)深入分析 1. 主要…...
pip下载包出现SSLError
报错: ERROR: Could not install packages due to an OSError: HTTPSConnectionPool(host‘files.pythonhosted.org’, port443): Max retries exceeded with url: /packages/8a/c2/ae7227e4b089c6a8210920db9d5ac59186b0a84eb1e6d96b9218916cdaf1/taming_transform…...
零成本的互联网创业创意有哪些?
在互联网时代,创业的门槛大大降低,即使没有大量的资金投入,也有许多机会可以实现创业梦想。以下将为您介绍一些零成本的互联网创业创意,帮助您在互联网的海洋中找到属于自己的宝藏。 一、内容创作与自媒体 (一&#…...
linux ubantu重启桌面
在 Ubuntu 系统中,重启桌面环境通常有几种方法,具体取决于你所使用的桌面环境(如 GNOME、KDE 等)。下面是几种常用的重启桌面的方法: 重启 GNOME 桌面环境 如果你使用的是 GNOME 桌面环境(Ubuntu 默认桌面…...

DeepSeek重新定义“Open“AI
“面对颠覆性技术,闭源所创造的护城河是暂时的。即使是OpenAI的闭源方法也无法阻止他人赶超。” ——梁文锋,DeepSeek CEO DeepSeek V3 是一个拥有6710亿参数的开源AI模型,正在提升AI效率的新标准。它在相对有限的预算下进行训练,…...
iOS - 自旋锁
在 Objective-C 运行时中大量使用自旋锁,主要有以下几个原因: 1. 性能考虑 上下文切换成本 // 自旋锁实现 static ALWAYS_INLINE void OSSpinLockLock(volatile OSSpinLock *lock) {do {while (lock->value ! 0) {__asm__ volatile ("pause&q…...
web应用网站如何启用http2请求
要启用 HTTP/2 协议,您需要确保您的 Web 服务器软件支持 HTTP/2,并进行相应的配置。以下是一些常见的 Web 服务器软件及其启用 HTTP/2 的方法: 1. Nginx 对于 Nginx,您需要确保使用的是 1.9.5 或更高版本,因为这些版本…...

python进阶06:MySQL
课后大总结 Day1 一、数据库命令总结 1.连接数据库 连接数据库进入mysql安装目录打开bin文件夹,输入cmd(此命令后无分号)mysql.exe -u root -ppassword命令后输入密码:root 设置密码set passwordpassword("root123"); 查看所有数据库show databases; …...
mac 使用zip2john破解zip压缩包密码
一、下载: git clone https://github.com/magnumripper/JohnTheRipper.git cd JohnTheRipper/src ./configure sudo make -s clean && sudo make -sj4 cd ../run二、使用: zip2john提取提取 ZIP 文件的哈希: ./zip2john protecte…...

若依中Feign调用的具体使用(若依微服务版自身已集成openfeign依赖,并在此基础上定义了自己的注解)
若依中Feign调用具体使用 注意:以下所有步骤实现的前提是需要在启动类上加入注解 EnableRyFeignClients 主要是为开启feign接口扫描 1.创建服务提供者(provider) 导入依赖(我在分析依赖时发现若依本身已经引入openfeign依赖,并在此基础上自定义了自己的EnableRyF…...
【算法题系列】LeetCode 5.最长回文子串|JavaScript 5种思路实现
题目描述 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2: 输入: "cbbd" 输出: &q…...
基于ROS先验地图的机器人自主定位与导航SLAM
2021年学习,当时参加科大讯飞的智能车大赛, 【语音交互启动-teb算法路径规划A*算法自动避障路径最短优化yolo5目标检测视觉结果判断分类终点指定点位自动泊车语音播报。】 【讯飞学院】http://www.iflyros.com/home/ 一、全局路径规划中的地图 栅格地图&…...
nginx 1.6.3配置虚拟主机与rewrite-location匹配规则
1、 Nginx 虚拟主机配置(配置文件末尾以分号[;]结尾) (1) 准备测试目录站点 [rootWEB conf]# cd /application/nginx/conf/ [rootWEB conf]# mkdir extra (创建虚拟主机存放目录࿰…...

1130-host ... is not allowed to connect to this MySql serve
局域网内另外一台电脑使用navicat连接Mysql出现上述问题:不允许连接 解决方案: 1、输入命令:进入mysql mysql -u root -p 2、输入命令:展示所有数据库 show databases; 3、输入命令进入mysql数据库: use mysql; 4、…...

力扣1502判断能否形成等差数列
class Solution:def canMakeArithmeticProgression(self, arr: List[int]) -> bool:# 对数组进行排序arr.sort()# 计算公差diff arr[1] - arr[0]# 从第二个元素开始逐个检查差值是否一致for i in range(1, len(arr) - 1):if arr[i 1] - arr[i] ! diff:return Falsereturn …...
Python版本变更历史及版本选择指南
Python版本变更历史及版本选择指南 Python版本变更历史及版本选择指南1. Python 3.13.1(2023年发布)主要特性适用场景 2. Python 3.12(2022年发布)主要特性 3. Python 3.11(2022年发布)主要特性 4. Python …...
初始值变量类型
状态名同步位置初始值变量类型不支持的UL刷新注意事项State父组件必填Object、classstring、number、boolean、enum类型,以及这些类型的数组。支持Date类型。对象的对象数组属性更新数组对象的属性更新 State装饰的变量必须初始化,否则编译期会报错。Sta…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...