我有尝试了一些方案,但是因为要依赖主项目的程序代码所以不是很顺利,我想到了一些解决方案,因为我要加载的代码脚本是纯函数所以问题不大,可以通过require的导入进来,我觉得达到我要的效果不是很难,至于bundle 这个功能不适合与开发mod,谢谢你的回答,欢迎加入cocos论坛

PC来说,就Bundle就可以了。只要代码做好规划,完全满足使用
bundle 怎么做到依赖主项目的情况下,不提供主项目源码可以让玩家制作mod
有接口声明就行吧,ts里使用微信的接口不就是
bundle 是被导入方,不是作为导入方。不行的。bundle加载主项目,不是这逻辑
MOD不需要依赖主项目
你可以看看AMXX这些框架
如果不依赖主项目的话,怎么运行的起来这个mod呢,不还是要启动一个主项目吗
其实提供主项目代码也没什么,编成简化合并版js或者加密混淆一下,再提供到mod模版工程里面。你可以看成其他游戏框架中,主项目提供native dll,Managed dll,jar一样的编译好的东西。这种是最方便的。不然就做专门的editor和外挂式mod框架,比较麻烦。
游戏的循环还是在跑的,最上层的window对象还是在的,你可以在最上层写一套消息系统,用来驱动逻辑,mod监听你规定好的消息,比如游戏开发,用户点击,然后触发mod自己的逻辑。
这一套很稳定的,CS插件就是这么做的,生化模式,大灾变都是在这个上面写出来的。
加载那些,只允许加载bundle自己的内部资源就好
消息机制简单,主要是mod本身的逻辑,需要调用主项目的一些暴露给mod的api,例如战斗场景查询,玩家数据,实体对象的功能等。这些需要有主项目存在才行,不管是二进制形式还是加密混淆形式都可以。
在开发mod的时候几种形式引用主项目:
1.mod的模版工程,工程内必须要嵌入一个主项目。不管是二进制的还是加密混淆的都可以,有的语言也可以只提供声明文件,但运行就需要了。
2.编辑器形式,编辑器内嵌主项目,或者主项目内嵌编辑器。
3.外挂式,专门的mod启动器,启动主项目进程,并且注入其中,并反射出里面的类对象供mod使用。
不需要啊,直接消息传递就好了,mod开发都是HOOK的方式
开发mod的时候不运行调试的吗? 需要主项目就是因为要运行起来。所以分发mod开发环境的时候也要分发主项目,哪怕是二进制文件,cs不也都是要先有游戏exe吗?
而且APi用消息也比较低效,高效的是给一个dll和接口,或者是反射,直接调用就可以。
主项目是主项目,游戏自己跑起来就好,不需要给额外的东西给MOD,在顶层留一个消息对象就好,进入游戏后写一个BUNDLE检查,依次加载BUNDLE代码
BUNDLE统一一个写法,依次注册消息。
完毕
register_plugin(“插件名”, “1.0.0.0”, “作者”);
// FM_PlayerPreThink是fakemeta_const.inc中的一个枚举常量,对应玩家预思考事件,是用于添加挂钩的事件索引.
// CS1.6一般都是每秒大约刷新100次屏幕画面,而玩家也会随着这个频率,每秒触发100次预思考事件(bot大约每秒25次).
// 玩家预思考事件很实用,在这里可以实时检查玩家的属性变化,或修改玩家属性.
// 只要将它用作register_forward函数的第1个参数,我们就能为玩家预思考事件添加挂钩.
// 挂钩的目标函数会接收指定事件的所有参数,充当自己的参数.
// 玩家预思考事件只有1个事件参数,就是思考者(玩家的实体索引).
register_forward(FM_PlayerPreThink, "PlayerPreThink_PreHook", 1);
// Ham_TakeDamage是ham_const.inc中Ham枚举的成员,对应实体受伤事件,是用于添加挂钩的事件索引.
// 任何实体只要受伤便会触发受伤事件,但不包括某些直接修改生命值的情况.
// 实体受伤事件的挂钩常常被用于修改伤害值,阻止受伤,统计伤害量等等.
// 只要将它用作RegisterHam函数的第1个参数,我们就能为实体受伤事件添加挂钩.
// RegisterHam函数的第2个参数,用于设定实体原始类名,只有原始类名与其相同,才能触发钩子的目标函数.
// 1.8.2或低版本amxx并没有第4个参数,该参数用于支持bot这种没有类名的玩家实体.
// 实体受伤事件会将自身所有参数(受害者,加害者,攻击者,伤害值,伤害类型位标志)传递给钩子的目标函数.
RegisterHam(Ham_TakeDamage, "player", "PlayerTakeDamage_PreHook", 0, true);
// PlayerPreThink_PreHook会接收1个来自玩家预思考事件的参数:玩家实体索引
public PlayerPreThink_PreHook(playerEntId)
{
server_print("实体索引为%d的玩家,触发了一次预思考事件", playerEntId);
}
// PlayerTakeDamage_PreHook会接收5个来自实体受伤事件的参数:受害者,加害者,攻击者,伤害值,伤 害类型位标志
public PlayerTakeDamage_PreHook(victimEntId, inflictorEntId, attackerEntId, Float:damage, damageFlags)
{
// 如果保留同位上的1之后不为0,则设定钩子目标函数的计算结果为拥有阻止事件功能的宏定义常量HAM_SUPERCEDE,并提前退出函数
if (damageFlags & DMG_FALL)
{
return HAM_SUPERCEDE;
}
return HAM_IGNORED;
}
可能你没理解我和上面那个w6166231 的意思,主项目就是游戏,游戏就是主项目的二进制形式,开发mod需要依赖这个游戏,也就是主项目的二进制形式。
然后这些注册的api,和枚举,其实就是主项目提供的接口
他说的是怎么把代码接口暴露给MOD,不需要。只需要构建一个完善的消息机制,把所有的数据对象放好,HOOK出来就好了
我前面也说了。提供二进制形式+接口就可以了。接口就是类似你这的fakemeta_const.inc。
回答楼主其实就是说,加密和编译后的代码其实也可以看成是二进制的,不需要那么严格,消息的形式需要包装,没那么方便。
消息通用百搭,可以跨主游戏。
比如我勾到一个引擎的参数,只要是用这个引擎的,都可以用。
这套哪怕是U3D,起源,都是如此
如果你真考虑所谓的“易用”
那就应该走 触发器逻辑 去构建项目
这样就真好用了
那其实算是注入式的那种了。主进程和mod相互发消息。
我看过比较多的是嵌入式的脚本,例如issac等,mod都会提供lua脚本,由游戏整体进行调度,游戏提供api。
注入外挂式的,可能需要跨引擎,不过类似这种脚本类的,基本上是每个游戏就每个游戏的做法。
另外杀戮尖塔也是注入式的,他就不是消息,而是用java反射来做的
那种只能算官方提供便利,需要开发者大量的时间去做。
但是在上面是整个游戏做成触发器+事件形式,游戏本身就是用这个去构成,游戏本身就是MOD的一部分,WAR3,OW都是类似。可以参考一下 我来自江湖
这种才是最优解。
云风的 BLOG: 虚拟文件系统的 mod 机制
就是我说的hook吧。游戏内部做好模块划分和加载。issac的hook都有上百个了,也是类似这种做法。但是本身是用lua来做hook和api注册的,相当于代码直接调用。
云风这个blog的内容,其实就是我前面说的虚拟文件系统VFS,主要是cocos用uuid做索引,所以做这一套会比较别扭。确实有目录覆盖的机制,但是资源分布已经打乱成uuid路径了,不好做一些覆盖。
我总感觉我这个话可能没讲清楚,hook可以是消息,比较简单。但是需要调用游戏内api,如果把api弄成消息,就很麻烦了。
我举个例子,就是你刚才的cs的脚本:
public PlayerPreThink_PreHook(playerEntId)
假如我现在要在这个函数里面查询,我周围20米有多少个敌人,我是不是可以调用:
enemyEntities = GetEnemyNearby(playerEntId,20.0)
那么GetEnemyNearby其实就是我说的场景查询api,如果这个场景查询api是以消息形式发给主项目并且同步回复,需要发送和接受2次消息,并且要传递数组,是不是就比较麻烦。
这种情况就是用直接的代码型接口比较方便,不好不依赖主项目的接口的,特别是场景上的接口。
