- 积分
- 10277
- 帖子
- 主题
- 精华
贡献- 份
爱心- 心
- 钻石
- 颗
- 人气
- 点
- 下界之星
- 枚
- 最后登录
- 1970-1-1
- 注册时间
- 2016-6-16
来自:辽宁 | 本帖最后由 森林蝙蝠 于 2019-7-27 23:11 编辑
所谓的coremod,是forge提供的代码注入机制,用来方便开发者修改原版代码或者控制mod加载用的,要实现coremod,首先应该实现IFMLLoadingPlugin接口:
public class WarhammerLoader implements IFMLLoadingPlugin { public String[] getASMTransformerClass(){ //代码注入需要用到asm,你的asm操作一般在IClassTransformer中进行,这个类用来获取实现了IClassTransformer的类 //LaunchClassLoader.registerTransformer()中IClassTransformer的实现会被ClassLoader加载
return new String[]{ "com.forestbat.warhammer.asm.ChebishevTransformer","com.forestbat.warhammer.asm.ParentTransformer" }; } public String getModContainerClass(){ //用来获取实现了ModContainer的类 return "com.forestbat.warhammer.asm.WarhammerModContainer"; } public String getSetupClass(){ //用来获取实现了IFMLCallHook的类 return null; } public void injectData(Map<String,Object> data){ //注入一些重要的数据,例如MC路径 data.put("mcLocation", File mcDir);//MC路径 data.put("coremodList", List loadPlugins);//coremod表单 data.put("runtimeDeobfuscationEnabled", !deobfuscatedEnvironment);//确认运行时反混淆开启 data.put("coremodLocation", File location);//coremod路径 } public String getAccessTransformerClass() { //AccessTransformer用来改变类的访问权限,例如从public改成private,forge已经自带 return "net.minecraftforge.fml.common.asm.transformers.AccessTransformer"; } }
getASMTransformerClass() 众所周知,Optifine的快速渲染是使用了自己的sin和cos算法,但可惜optifine是不开源的,那我们能不能实现自己的算法,以图对原版的算法实现优化呢?答案是肯定的。 在这里跟大家介绍一下切比雪夫逼近:切比雪夫多项式(百度百科) 比起泰勒公式,切比雪夫的优势在于可以以更低的次数逼近目标函数,降低运算量。出于简洁,使用第一类切比雪夫多项式去逼近sin(x),也就是sin(x)=aT0(x)+bT1(x)+cT2(x)+……这样的形式。 为了保证精度,我们选用六次切比雪夫逼近式,可以达到误差限4e-7,远高于原版算法的精度1e-6级别,最后的公式如下:
- <font color="Black">sin = (((((-0.000960664 * value + 0.0102697866) * value - 0.00198601997) * value - 0.1656067221)
- * value - 0.0002715666) * value + 1.000026227) * value;
- //value是传入的弧度参数,范围为(0,π/2),其他角度都可以通过诱导公式变过来
- </font>
复制代码 什么你问我怎么算出来的?取特殊点(例如sin(pi/6)=1/2)带入你的逼近多项式,得到一个7元一次方程组,然后求解这个方程组对应的矩阵(如何用Excel求逆矩阵和矩阵相乘可百度),即可得出a,b,c等相应系数。
好的,式子得到了,那么是时候将其置入游戏体验一下了。 如果你只是给自己的mod用一下,就不会有接下来的东西,直接声明一个static方法调用即可;如果你像我一样的“大公无私”,想让原版游戏的算法也变成这样大家一起用,下面的内容或许就很重要了。 准备好的了话,请看下面↓↓↓或这里- <font color="Black">public class ChebishevTransformer implements IClassTransformer {
- //IClassTransformer:MC自带的launchwrapper工具所提供的类转换接口
- @Override
- public byte[] transform(String name, String transformedName, byte[] basicClass) {
- if (transformedName.equals("net.minecraft.util.math.MathHelper")) {
- FMLLog.log.warn("[Transforming...]");
- //在这里输出一个告警信息,告诉大家你在做什么,可选项
- ClassNode classNode=new ClassNode(ASM5);
- //声明一个ASM5的classNode
- ClassReader classReader=new ClassReader(basicClass);
- //声明一个读取basicClass的classReader
- classReader.accept(classNode,0);
- //让classReader接收到要修改的classNode/classVisitor,以产生事件
- List<MethodNode> methodNodeList=new ArrayList<>(classNode.methods);
- //由于我们的目标是要修改方法,因此要遍历一下MethodNode,将其保存在list中
- for(MethodNode methodNode:methodNodeList)
- if (methodNode.name.equals("func_76126_a(F)F")) {
- //上文的transformedName是转换后的类名,因为你看到的代码是根据notch name反混淆出来的,methodName是它的srg name, //(F)F指参数为float,返回float
- methodNode.instructions.clear();
- //清除methodNode下的结构
- methodNode.instructions.add(new InsnNode(FLOAD))
- //添加你自己的实现,FLOAD代表传入float参数,其在ASM中规定的操作码(Opcode)是23
- methodNode.instructions.add(new MethodInsnNode
- (ACC_PUBLIC + ACC_STATIC, "Lcom/forestbat/warhammer/asm/ChebishevTransformer", "sin", "(F)D", false));
- //添加上你自己的实现,ACC_PUBLIC,ACC_STATIC是ASM中public和static的操作码(分别是0x0001和0x0008);
- //“Lcom/forestbat/warhammer/asm/ChebishevTransformer”是owner参数,表明方法的源头类;
- //“sin”是name参数,表明我们的方法名;
- //“(F)D”是“float形参,返回double”的description(描述符);
- //false表明了owner类(不是)一个接口,若是,则为true
- methodNode.instructions.add(new InsnNode(DRETURN));
- //DRETURN代表返回double值,其操作码是175
- }
- ClassWriter classWriter = new ClassWriter(2);
- //声明一个classWriter写入内容,参数2的意义是COMPUTE_FRAMES,自动计算局部变量与操作数栈
- //每当方法被调用一次栈上就会生成一个帧,帧又被分为局部变量和操作数栈,COMPUTE_FRAMES会帮你自动计算,但会拖慢性能
- classNode.accept(classWriter);
- //classNode接收classWriter做参数,向方法内部生成二进制代码
- return classWriter.toByteArray();
- }
- return basicClass;
- //返回被修改的basicClass
- }
- </font>
复制代码 //我们自己的sin方法 public static double sin(float value){ if(value>=0&&value<=PI/2) return (((((-0.000960664 * value + 0.0102697866) * value - 0.00198601997) * value - 0.1656067221) * value - 0.0002715666) * value + 1.000026227) * value;
if(value<0){ return -sin(-value); } else{ return sin((float)(value%PI)); } } 首先解释下,为什么basicClass的类型是byte[]——因为basicClass在这里指的是未加载的类字节码,字节码本质上还是一串字节,因此类型是byte[]。 asm修改字节码的步骤是固定的,ClassReader读取basicClass(使之可被修改),接收ClassNode(来自asm的tree api)/ClassVisitor(来自asm的core api),而ClassNode/ClassVisitor又会接受ClassWriter,将开发者的改动通过ClassWriter写入Class中,最后返回修改后的basicClass。
tree api的性能低于core api,我们不妨康康core api的写法: public class ParentTransformer implements IClassTransformer {//一个修改父类的轮子 private static String parentName; private static String[] baseInterfaces;
@Override public byte[] transform(String name, String transformedName, byte[] basicClass) { if(baseInterfaces!=null && parentName!=null){ ClassReader classReader = new ClassReader(basicClass); ClassWriter classWriter = new ClassWriter(2);//2代表自动计算栈帧 ClassVisitor classVisitor = new ClassVisitor(ASM5, classWriter) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(52, access, transformedName, null, null, null); //52代表Java8,access代表public/private/protected,superName即父类,由于我们要修改父类,因此原类不应有父类,否则会发生错误 } }; classReader.accept(classVisitor, 2); classWriter.visit(52, 0x0001, transformedName, null, setParentName(transformedName, parentName), setInterfacesName(transformedName, baseInterfaces)); classWriter.toByteArray(); return basicClass; } else return basicClass;
} public static String setParentName(String transformedName,String parentName){ ParentTransformer.parentName=parentName; return parentName; } public static String[] setInterfacesName(String transformedName,String[] baseInterfaces) { ParentTransformer.baseInterfaces=baseInterfaces; for (String baseInterface : baseInterfaces) { try { ClassReader classReader = new ClassReader(baseInterface); if (classReader.getItemCount() == 0) return baseInterfaces; }catch (IOException e){FMLLog.bigWarning("No any interfaces!");} } return null; } }
getModContainerClass() 每一个被@Mod注解标记从而被认为是mod的mod,都会有自己的ModContainer,如无意外将会在FMLModContainer类中进行处理,读取@Mod注解中的信息,但是开发者也可以自己改写ModContainer。 public class YourModContainer implements ModContainer { private ModMetadata modMetadata; private boolean enabled=true; private VersionRange versionRange; private ModContainer modContainer; public String getModId(){ return MOD_ID; } public String getName(){ return MOD_NAME; } public String getVersion(){ return VERSION; } public File getSource(){ Path path= Paths.get(".minecraft","mods",getName()); return path.toFile(); } public ModMetadata getMetadata(){ return modMetadata; //ModMetaData就是mod的“标签”,由@Mod注解信息决定,也可以由玩家自己编写 }
@Override public void bindMetadata(MetadataCollection mc) { try { ModMetadata metadata = mc.getMetadataForId("Gregtech", Maps.newConcurrentMap()); if (metadata != null & metadata.dependencies.contains("ic2")) getUpdateUrl().openConnection(); }catch (IOException e){LOGGER.info("IC2 WebSite Error!");}}
@Override public void setEnabledState(boolean enabled) { this.enabled=enabled; } public Set<ArtifactVersion> getRequirements(){ return modMetadata.requiredMods; } public List<ArtifactVersion> getDependencies(){ return modMetadata.dependencies; } public List<ArtifactVersion> getDependants(){ return null; }
@Override public boolean registerBus(EventBus bus, LoadController controller) { bus.unregister(ForgeRegistries.BIOMES);//从EventBus移除所有生物群系,当然这么做很扯淡 controller.getActiveModList().remove("titan");//从活跃加载mod列表中移除泰坦,换句话说,禁用了泰坦 return true; }
@Override public boolean shouldLoadInEnvironment() { return false; }
@Override public String getSortingRules() { //forge使用的是拓扑排序(TopologicalSort),当然你可以写一个自己的 return “com.yourmod.yourSortList"; }
@Override public boolean matches(Object mod) { return mod.equals(YourMod.INSTANCE); }
@Override public Object getMod() { return YourMod.INSTANCE; }
@Override public ArtifactVersion getProcessedVersion() { return new DefaultArtifactVersion(YourMod.VERSION); } public boolean isImmutable(){ return false; }
@Override public String getDisplayVersion() { return YourMod.VERSION; }
@Override public VersionRange acceptableMinecraftVersionRange() { try { versionRange=VersionRange.createFromVersionSpec("[1.12,1.12.2]");//限定你的mod版本范围是1.12-1.12.2 }catch (InvalidVersionSpecificationException e){FMLLog.bigWarning("No 1.12!");} return versionRange; }
@Nullable @Override public Certificate getSigningCertificate() { Certificate[] certificates = getClass().getProtectionDomain().getCodeSource().getCertificates(); return certificates != null ? certificates[0] : null; }
@Override public Map<String, String> getCustomModProperties() { return null; //向description中添加自己的信息,forge本身不一定会作出处理,这一部分由开发者自行处理,与下文descriptor并不一样 }
@Override public Class<?> getCustomResourcePackClass() { return null; }
@Override public Map<String, String> getSharedModDescriptor() {Map<String, String> descriptor = Maps.newHashMap(); descriptor.put("modsystem", "FML"); descriptor.put("id", getModId()); descriptor.put("version", getDisplayVersion()); descriptor.put("name", getName()); descriptor.put("url", modMetadata.url); descriptor.put("description", modMetadata.description); return descriptor; }
public Disableable canBeDisabled(){ return Disableable.NEVER;//设置其不会被禁用 }
@Override public String getGuiClassName() { return null; }
@Override public List<String> getOwnedPackages() { return null; }
@Override public URL getUpdateUrl() { try { return new URL("https://minecraft.curseforge.com/projects/botania/files/2524591");//方便所以打了个botania的更新链接 }catch (MalformedURLException e){} }
@Override public int getClassVersion() { return 52;//52代表Java8 } public void setClassVersion(int version){ } }
getSetupClass() 由于IFMLCallHook这个接口的调用时间非常早,类似于.NET Core的startup接口,所以这里不应该出现MC本身的代码(因为MC代码的反混淆还在IFMLCallHook之后) public class WarhammerHook implements IFMLCallHook { @Override public void injectData(Map<String, Object> data) { liveEnv = (Boolean)data.get("runtimeDeobfuscationEnabled"); cl = (LaunchClassLoader) data.get("classLoader"); File mcDir = (File)data.get("mcLocation"); fmlLocation = (File)data.get("coremodLocation"); ClassPatchManager.INSTANCE.setup(FMLLaunchHandler.side()); FMLDeobfuscatingRemapper.INSTANCE.setup(mcDir, cl, (String) data.get("deobfuscationFileName")); }
@Override public Void call() throws Exception { Class<?> classTitan=Class.forName("titan(or its obfuscate name)",false, (LaunchClassLoader)cl) //LaunchClassLoader是MC自带的类加载器 CodeSource codeSource = classTitan.getProtectionDomain().getCodeSource(); Files.delete(Paths.get(codeSource.getLocation().toURI())); //探测到泰坦所在的类后删除它 } }
最后 我们的东西已经写完了,应该加载到游戏中使之生效。 打开你的build.gradle文件,向里面写入这样一段:
- jar {
- manifest {
- //实现了IFMLLoadingPlugin的类(你的coremod类)
- attributes 'FMLCorePlugin': 'com.forestbat.warhammer.asm.WarhammerLoader'
- attributes 'FMLCorePluginContainsFMLMod': 'true'
- //access transformer配置文件,一般是modid_at.cfg,放在resources/META_INF文件夹下
- attributes 'FMLAT': 'warhammer_at.cfg'
- //https://github.com/Ipsis/Woot/blob/1_12/src/main/resources/META-INF/woot_at.cfg(woot的AT配置文件)
- }
- }
复制代码
———————————————————————— 分割线 —————————————————————————— 如有需要继续更新ITweaker相关
来自群组: HAYO Studio |
评分查看全部评分
|