为什么我的MC进mc没有mod选项项后会闪退...

> 为什么我的minecraft装光影之后上游戏会闪退
补充:就是启动游戏之后出现MOJANG就闪退 满意答案
可能是电脑的配置,也有可能是mod冲突了吧,这个对电脑配置要求很高的= =
不关我配置问题..毕竟我以前开过 不知道为什么现在会闪退
以前开过那为什么现在会闪退呢?是不是改了什么东西啊= =
并没有改什么东西QAQ
真的是怀疑你的内存够不够。。。。内存开2g以上,把材质包删了,打开再还原,或者你游戏有问题
是不是你装了mod然后和光影冲突?
mod是有的但我并不觉得我的mod跟光影有冲突
给你看一下
= =在期间你下过的mod一个一个删了试试,我以前也有过,应该有冲突,要不换个启动器。。
不能删啊。。我这mod都服务器的删了就进不去了
好吧我尽力了= =。。。。:实在不行的话还是用一下低配光影也不错才占1G内存
就是装了光影闪退...
...........................................我也不知道你的电脑= =以前可以现在不可以不科学啊,要不就是有冲突,要不就电脑配置,没别的了= =
其它回答 你是把光影Mods拖动进Forge的Mods文件夹还是单独安装?拖动进Mods会有很多Bug,并且可能硬件有不支持,内存必须8GB以上,4GB勉强能玩,处理器,显卡也要好,有人开了光影显卡温度超高。
前面说了啊。。不关我配置事..
你的配置发来看看
以前开过的但是不知道为什么现在装光影会闪退
可能Java内存设置有问题,我的内存设置出问题也会这样。或者缺失文件,重新安装可能就好了
其它回答 你好,你的问题可能是你的电脑配置不行,但是这个几率很小。也可能是你启动时MC里的内存溢出。建议你在设置里面调试一下内存。或者换一个版本的MC。有可能是你的光影和游戏版本不匹配。
其它回答 因为材质包太黄,手机被迷住了
其它回答 电脑支持不起23333
发表我的评论[疑难解答]为何启动MC我的世界客户端时闪退 _ MC服主网
本站为了您愉快的极速浏览开启了缓存功能,您的评论不会在第一时间显示并刷新,敬请谅解!若有疑问可在节假日QQ咨询站长!
来到MC服主网必看原创文章集锦,务必要看——
MC服主网已经完成备案开始正式运营,欢迎各位服主积极投稿哦~投稿一经采用奖励丰厚!发送你的稿件到吧!
站长与您一起打造全中国最全面的我的世界服务器服主博客!网站内容建设中,欢迎订阅以获取关于MC的最新资讯!
访问本站最好使用Chrome等现代化浏览器以获取最佳浏览效果,本站教程大部分为优质图文教程,欢迎来点评我哦!
支持站长?全站广告欢迎点击~需要帮助?QQ联系站长~赞助本站?~
> [疑难解答]为何启动MC我的世界客户端时闪退
启动Minecraft时闪退估计是很多人都遇到过的事情,具体表现为启动器中点击启动游戏后没有反应,抑或是点击后出现Java报错窗口像这样的,要么就是在根目录出现hss_****_error.log类似的文件表明游戏客户端崩溃了。
我个人是十分反对Mojang用Java做开发环境来做Minecraft,特别是跟微软合作之后有可能开发X86版本针对Windows的专用客户端,但现在讨论这些为时尚早,还是先解决好眼前的问题再说。
在玩MC之前你应该先了解一下何为Java,下面引用百度百科——
Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
那么在客户端运行时到底会出现哪些问题呢?
1、首先,不同的MC版本一般要求不同的Java版本,1.7.2及以下的MC大部分不兼容最新版的Java8,表现为点击启动游戏后没有反应,任务管理器“进程”列表中javaw.exe进程一闪而过,没有继续加载。这种情况很简单,一般卸载原Java后重新安装Java7即可。这里要注意的是官网目前已经不再提供老版本的Java7下载了,但是并不意味着Java7被停用了,MC服主网这里给出Java7 32位版本的链接给大家下载,注意哦Java对系统要求并不严格,64位操作系统可以安装32位Java并且兼容性良好,所以强烈建议各位服主将Java 32位版本开放给大家下载,MC服主网也会在今后介绍如何配置免安装Java版的MC客户端哦!由于本站自建云盘因欠费400余元(泥萌太热情了)导致云盘在今年6月之前上传的数据全部丢失,目前站长已经把收集到的数据全部上传,如仍有缺失请尽快告知!
2、其次,如果出现这样的窗口说明你的Java内存设置有误,一般32位Java内存设置不能超过2GB,请检查启动器内存设置。
3、游戏崩溃这个问题就比较广泛,这时您可以要求玩家将错误日志文件发送给您,然后在百度上面搜索有关的关键字即可,实在不行可以联系站长哦!
好了今天的疑难解答就到这里了哦~
如未注明 , 均为原创丨转载请注明本文来自!
由于本站属于分享类网站,故依据CC BY-NC-SA 3.0协议,收集了大量网络资源,如原作者对本站转载的资源不满意的话,可以联系我们进行删除处理,同时本站对原创作品享有相关法律权利。
联系邮箱【admin#mcfuzhu.net】【#换成@】
由于本站自建云盘因欠费400余元(泥萌太热情了)导致云盘在今年6月之前上传的数据全部丢失,目前站长已经把收集到的数据全部上传,如仍有缺失请尽快告知!
or分享 (0)装mod之后崩溃bug冲突以及id调整方法_魔方网我的世界专区
你正在访问:
装mod之后崩溃bug冲突以及id调整方法
在我的世界游戏中装MOD难免会遇到各种闪退以及BUG冲突、id冲突,今天小编为大家带来的是我的世界闪退以及BUG冲突、id冲突解决办法,希望可以帮助到喜欢玩模 ...
  很多小伙伴安装很多mod后,会遗憾地发现因为mod间冲突而导致无法正常游戏。各种闪退,BUG以及冲突id,这些问题怎么解决呢?还不知道该如何解决这些问题的小伙伴不妨看看小编为大家精心准备闪退,BUG以及冲突id的调整方法。
  本文摘自魔方网论坛:
  ID冲突原因以及处理办法:
  块id冲突:
  原版留下的一共133个块id的空间。很多mod增加了新的块,占据了某些原本空白的id。有些mod的id为一个默认值,有的则可以检测空白id来进行自动分配。但两个mod试图占据同样的块id时会导致冲突。通常的结果是crash,游戏崩溃,无法启动。绝大部分mod的配置文件都提供了配置id的可能。请找到其配置文件修改其 block id。
  如ic2(工业2mod)的配置文件为.minecraft/config/IC2.cfg,用记事本或者其他软件打开它,在block{}间的那些设置即ID设置。等号左边为ic2 mod增加的新的方块,右边数字为当前它占据的id。可以自由修改它。记住默认125到255是空白的方块id。
  建议大家少装一些MOD,因为当安装的mod很多时,分配id变得非常困难,因此我们可以使用一些辅助的办法。
  安装TMI或者NEI在游戏内可以方便地查看已经使用的id。
  NEI更是提供了导出当前id使用情况的功能(在道具栏界面,点击左下角的选项,在出现的菜单的左下角点击两次,即可导出,会英语的自己看也应该明白我的意思吧,还是不明白的可以自己查一下那几个单词的意思)。
  一个小技巧:安装RP2放在最后安装可以解决一些常见的id冲突,因为这个占用块id多达17个的mod可以完美地自动分配id。
  道具id冲突:
  道具id冲突通常是隐形的,通常不会导致游戏无法开启。但会使得你无法正常使用对应id的物品。
  同样可以在配置文件中修改其id。
  一个查看道具是否冲突的好办法是使用内置合成表mod,观察新增加mod的合成公式是否全部正常。
  另一个办法是万能的NEI。到处空余道具id看是否默认分配有冲突即可。
  修改了同样的class文件:
  这些冲突有的会直接导致游戏崩溃,有的则导致一些mod的功能无法完全实现。最好的解决办法是修改他们使用的文件。另一个办法是将他们都安装到jar里,功能失效的文件放在后面安装(不一定有效,可能导致崩溃)。如果非常希望这两个mod能够一起安装,请试图与作者联系咨询是否有可用的兼容补丁。
  其他问题:
  BUG恒久远,闪退永流传。任何mod可能导入新的bug与冲突。请学会在config/modloader.cfg文件中禁用引起冲突的部分。请在发现冲突后查看作者的原帖说明以及最近回复,通常都会提到比较明显的bug修正方法。
  最后需要特别提醒的是,发生冲突后,请仔细检查你使用的mod版本,minecraft版本,水桶版本以及各种API版本,确定它们为同一个版本设计,如果不是,明显的=0=你已经二了。
  一张ic2.cfg的配置文件截图:
  服务端mod问题:
  服务端和客户端版本的块id,道具id等必须保持同步的设置,否则会导致冲突。服务端通常同样有一个配置文件。一般服务端安装mod的步骤稍微不同。
  同样使用解压缩软件打开你的服务器主文件。通常是一个jar格式的文件。
  安装modloaderMP服务端版本和Forge服务端版本,注意不要删除META-INF文件夹。
  开启一次服务器生成mods文件夹和配置文档。
  安装其他mod同单机版本。
  配置id与客户端保持同步。
  如果单机没有安装服务端又的mod将无法进如服务器并提示缺失
  如果服务端没有单机安装的mod,那么将无法在服务器中正常使用相关mod的道具,最常见的可能是:数据流断开错误。
  id不同步导致一打错误,最常见的是块的功能和合成发生诡异的变化。甚至导致更严重的坏图问题。
  其他需要注意的地方:
  某些非forge版本的mod安装要求不同。如比狼好。因此在安装mod前最重要的事情是查看作者发布帖的说明以及下后附带的安装说明。
  准备需要安装的mod。如可能请仔细阅读作者关于其mod的说明,弄清版本以及安装需求,这样可以避免很多不必要的错误。
  以上有魔方网小编三易为大家带来,祝大家玩得开心,更多相关精彩尽在魔方网我的世界专区。
  我的世界论坛:&
  相关推荐:
突破经典的飞行射击类精品手机游戏。继承了经典飞机大战简单爽快的操作体验,玩法更多样。这么好玩的游戏,确定不玩吗?MC打不开/闪退问题的通用解决方法_minecraft吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:1,341,290贴子:
MC打不开/闪退问题的通用解决方法
最近首页实在太多mc打不开/闪退的提问帖了,希望这贴能够帮助大家    ------提问请移步置顶贴里的新人问答贴,单独开贴的问题可能会被删除
刚看到吧友说是首饰特殊...
绝对有效 楼主已经可以...
鉴于之前镇楼图那啥,所...
吴莫愁设计款,杜蕾斯分享爱公益礼盒,和爱人零距离,和公益零距离。
1.装j8玩j8。如果你去官网下载了最新的java,那么你八成打不开装有forge的版本,特点是点击启动游戏后无任何反应。其实j8还是支持一些高版本的forge的,低版本j8也是支持全版本的forge的,但是从各方面考虑,我还是选择安装7。如何确定自己安装了8呢,java路径里会显示jre1.8.0之类的字样,就像这样:何处寻找7?请一路跟着红字我该选择哪个下载?强烈推荐脱机下载(联机谁用谁知道),根据你系统的位数选择32位或者64位。
2.老爷,mc又崩溃了!如果你的mc打不开/闪退,不妨看看.minecraft/crash-reports文件夹里是否生成了崩溃文件。文件名是对应的崩溃时间。看懂崩溃报告?你可以来这个帖子对比你的崩溃报告里面没有你的崩溃类型的话你也可以试着把崩溃报告发给我,当然,我不一定能看得懂。更多问题欢迎来到查看或者前往置顶提问帖提问。
3.虚拟机?完全不懂!还有可能是这种情况,记得当初一个玩家死活打不开mc,最后他说他已经解决了,因为他使用的是虚拟机所以没有透露解决方法,如果你也是这样的情况请节哀哈
楼主还有一种可能,就是杀毒软件把游戏杀了,关闭杀毒软件就可以了,我就是这种情况
mc正版启动器怎么改路径=。=
服主同志,那一输入密码登陆之后直接关闭游戏怎么办==
还是不行怎么破
1.8.2和1.8.2快照进不去1.8.1正常无误,已经换过,无崩溃报告,调内存请自己面壁
还有个十分常见的,右键桌面→属性→设置→高级→疑难解答在这里有个,默认是调到第五档,但在这个档是打不开mc的(不道为啥。。。巨坑啊),得调到第二档才行
挂机一个月得百万,你还蓝瘦香菇么?
4.错版本mod搞得鬼!因为1.6以后mc出现新机制,既可以加载在.minecraft目录下的mods文件夹,又可以加载versions里的mods文件夹。而选择前者可能会在更换versions时导致mod与version不符,导致闪退。然而,这种闪退可能不会生成崩溃报告。得通过log文件查看
fml-client-latest:[19:37:20] [launcher/INFO] [FML/]: Forge Mod Loader version 7.10.25.1202 for Minecraft 1.7.10 loading[19:37:20] [launcher/INFO] [FML/]: Java is Java HotSpot(TM) Client VM, version 1.8.0_31, running on Windows 8.1:x86:6.3, installed at C:\Program Files (x86)\Java\jre1.8.0_31[19:37:20] [launcher/DEBUG] [FML/]: Java classpath at launch is /E://User data/Desktop/MC/Minecraft1.7.10简单整合/HMCL-2.2.1./E:/Ghost/User data/Desktop/MC/Minecraft1.7.10简单整合/lib/gson-2.2.4./E:/Ghost/User data/Desktop/MC/Minecraft1.7.10简单整合/lib/MetroLookAndFeel./E:/Ghost/User data/Desktop/MC/Minecraft1.7.10简单整合/lib/HMCLAPI.jar[19:37:20] [launcher/DEBUG] [FML/]: Java library path at launch is E:\\User data\Desktop\MC\Minecraft1.7.10简单整合\.minecraft\versions\1.7.10简单整合\1.7.10简单整合-natives[19:37:20] [launcher/DEBUG] [FML/]: Enabling runtime deobfuscation[19:37:21] [launcher/DEBUG] [FML/]: Instantiating coremod class FMLCorePlugin[19:37:21] [launcher/DEBUG] [FML/]: Added access transformer class cpw.mon.asm.transformers.AccessTransformer to enqueued access transformers[19:37:21] [launcher/DEBUG] [FML/]: Enqueued coremod FMLCorePlugin[19:37:21] [launcher/DEBUG] [FML/]: Instantiating coremod class FMLForgePlugin[19:37:21] [launcher/DEBUG] [FML/]: Added access transformer class net.minecraftforge.transformers.ForgeAccessTransformer to enqueued access transformers[19:37:21] [launcher/DEBUG] [FML/]: Enqueued coremod FMLForgePlugin[19:37:21] [launcher/DEBUG] [FML/]: All fundamental core mods are successfully located[19:37:21] [launcher/DEBUG] [FML/]: Discovering coremods[19:37:21] [launcher/INFO] [LaunchWrapper/]: Loading tweak class name cpw.mon.launcher.FMLInjectionAndSortingTweaker[19:37:21] [launcher/INFO] [LaunchWrapper/]: Loading tweak class name cpw.mon.launcher.FMLDeobfTweaker[19:37:21] [launcher/INFO] [LaunchWrapper/]: Calling tweak class cpw.mon.launcher.FMLInjectionAndSortingTweaker[19:37:21] [launcher/ERROR] [LaunchWrapper/]: Unable to launchjava.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(Unknown Source) ~[?:1.8.0_31] at java.util.ArrayList$Itr.remove(Unknown Source) ~[?:1.8.0_31] at net.minecraft.launchwrapper.Launch.launch(Launch.java:117) [launchwrapper-1.9.jar:?] at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.9.jar:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_31] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_31] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_31] at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_31] at org.jackhuang.hellominecraft.launcher.Launcher.main(SourceFile:122) [HMCL-2.2.1.exe:?]fml-junk-earlystartup:[16:33:40] [launcher/INFO] [LaunchWrapper]: Loading tweak class name cpw.mon.launcher.FMLTweaker[16:33:40] [launcher/INFO] [LaunchWrapper]: Using primary tweak class name cpw.mon.launcher.FMLTweaker[16:33:40] [launcher/INFO] [LaunchWrapper]: Calling tweak class cpw.mon.launcher.FMLTweaker[16:39:04] [launcher/INFO] [LaunchWrapper]: Loading tweak class name cpw.mon.launcher.FMLTweaker[16:39:05] [launcher/INFO] [LaunchWrapper]: Using primary tweak class name cpw.mon.launcher.FMLTweaker[16:39:05] [launcher/INFO] [LaunchWrapper]: Calling tweak class cpw.mon.launcher.FMLTweaker[16:41:48] [launcher/INFO] [LaunchWrapper]: Loading tweak class name cpw.mon.launcher.FMLTweaker[16:41:48] [launcher/INFO] [LaunchWrapper]: Using primary tweak class name cpw.mon.launcher.FMLTweaker[16:41:48] [launcher/INFO] [LaunchWrapper]: Calling tweak class cpw.mon.launcher.FMLTweaker[16:43:12] [launcher/INFO] [LaunchWrapper]: Loading tweak class name cpw.mon.launcher.FMLTweaker[16:43:12] [launcher/INFO] [LaunchWrapper]: Using primary tweak class name cpw.mon.launcher.FMLTweaker[16:43:12] [launcher/INFO] [LaunchWrapper]: Calling tweak class cpw.mon.launcher.FMLTweaker[19:37:20] [launcher/INFO] [LaunchWrapper]: Loading tweak class name cpw.mon.launcher.FMLTweaker[19:37:20] [launcher/INFO] [LaunchWrapper]: Using primary tweak class name cpw.mon.launcher.FMLTweaker[19:37:20] [launcher/INFO] [LaunchWrapper]: Calling tweak class cpw.mon.launcher.FMLTweaker
换成1.8.1的同样打不开QAQ
下了个1.7.5的试了下,老问题这是最新的log[00:15:07] [main/INFO]: Setting user: teacher[00:15:07] [main/INFO]: (Session ID is token:${auth_access_token}:${auth_uuid})[00:15:09] [Client thread/INFO]: LWJGL Version: 2.9.1[00:15:09] [Client thread/ERROR]: Couldn't set pixel formatorg.lwjgl.LWJGLException: Pixel format not accelerated at org.lwjgl.opengl.WindowsPeerInfo.nChoosePixelFormat(Native Method) ~[lwjgl-2.9.1.jar:?] at org.lwjgl.opengl.WindowsPeerInfo.choosePixelFormat(WindowsPeerInfo.java:52) ~[lwjgl-2.9.1.jar:?] at org.lwjgl.opengl.WindowsDisplay.createWindow(WindowsDisplay.java:252) ~[lwjgl-2.9.1.jar:?] at org.lwjgl.opengl.Display.createWindow(Display.java:306) ~[lwjgl-2.9.1.jar:?] at org.lwjgl.opengl.Display.create(Display.java:848) ~[lwjgl-2.9.1.jar:?] at org.lwjgl.opengl.Display.create(Display.java:757) ~[lwjgl-2.9.1.jar:?] at azl.ad(SourceFile:312) [1.7.5.jar:?] at azl.f(SourceFile:696) [1.7.5.jar:?] at net.minecraft.client.main.Main.main(SourceFile:148) [1.7.5.jar:?]
Catalyst™ 安装管理器安装报告03/05/15 02:53:53硬件信息名称ATI Mobility Radeon HD 4200 Series制造商Advanced Micro Devices, Inc.设备 ID0x9712厂商 ID0x1002分类代码0x030000修订 ID0x00子系统 ID0x21ca子系统厂商 ID0x17aa其它硬件 现有软件包AMD Catalyst 安装管理器AMD APP SDK RuntimeAMD Accelerated Video TranscodingAMD 拖放代码转换系统AMD Media Foundation AMD VISION Engine Control Center 可安装的软件包AMD Catalyst 安装管理器最终状态:成功项目版本:8.0.877.0大小:20 MBAMD 显示驱动程序最终状态:失败项目版本:8.970.100.7000大小:90 MB 其它检测到的设备 制造商Advanced Micro Devices, Inc.设备 ID0x9601厂商 ID0x1022分类代码0x060000修订 ID0x00子系统 ID0x21ca子系统厂商 ID0x17aa 制造商Advanced Micro Devices, Inc.设备 ID0x9602厂商 ID0x1022分类代码0x060400修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x970f厂商 ID0x1002分类代码0x040300修订 ID0x00子系统 ID0x9712子系统厂商 ID0x1002 制造商Advanced Micro Devices, Inc.设备 ID0x9604厂商 ID0x1022分类代码0x060400修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x9605厂商 ID0x1022分类代码0x060400修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 ATI Integrated SATA Support制造商Advanced Micro Devices, Inc.设备 ID0x4391厂商 ID0x1002分类代码0x010601修订 ID0x00子系统 ID0x21ca子系统厂商 ID0x17aa ATI Integrated SMBus Support制造商Advanced Micro Devices, Inc.设备 ID0x4385厂商 ID0x1002分类代码0x0c0500修订 ID0x42子系统 ID0x0000子系统厂商 ID0x0000 ATI Integrated Azalia Support制造商Advanced Micro Devices, Inc.设备 ID0x4383厂商 ID0x1002分类代码0x040300修订 ID0x40子系统 ID0x21ca子系统厂商 ID0x17aa 制造商Advanced Micro Devices, Inc.设备 ID0x4384厂商 ID0x1002分类代码0x060401修订 ID0x40子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x1200厂商 ID0x1022分类代码0x060000修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x1201厂商 ID0x1022分类代码0x060000修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x1202厂商 ID0x1022分类代码0x060000修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x1203厂商 ID0x1022分类代码0x060000修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 制造商Advanced Micro Devices, Inc.设备 ID0x1204厂商 ID0x1022分类代码0x060000修订 ID0x00子系统 ID0x0000子系统厂商 ID0x0000 错误信息 驱动程序安装:没有为匹配的设备安装指定的驱动程序软件包
java1.8我1.6.2完美运行,1.7.2点了运行游戏就没了,等好久都没弹出mc,求破,对了,1.7.2整合包和原版都是了,一样的
非常感谢 下载7试试 真的是日了狗了
启动器打不开呀,一点开启动器就停止,系统是W7,安装的JAVA7,而且原来的1.5都可以玩
这个怎么办啊?下了俩版本都是出现这个。然后又下了NET文件和那个J什么的。都不好使啊
能给我个这种下载的网址吗我用的是浩方卡。。
应用吧活动,
打开闪退根目录奔溃报告
是Java7 内存900 路径没错 这是什么问题 一直定在这里 网络也没问题 求大神楼主帮忙
1.8.x可以运行但是其他不行
目测是,可是官网下不了Java7 T T 楼主求助嘤嘤嘤
=-=从官网下载的 然后就没有选择路径
游戏 一打开就这要怎么办,,,,
什么启动器都是直接闪退肿么破
旋律启动器启动不了MC,别的就行,旋律启动器是最新版的
mc1.8安装forge之后打不开电脑版本是win7 64位,用的Java7 64位,旋律启动器(mcluncher),mc版本1.8,没有任何别的mod。在启动器里选择1.8原版能正常,选带forge的版本就启动不了。在versions文件夹里也有forge的文件夹,就是不能启动,这个怎么解决?求lz帮忙,想自己装mod结果连forge也装不好。
贴吧热议榜
使用签名档&&
保存至快速回贴MinecraftMod常见问题 | 白玉楼之梦
这篇文章收集了这四年间被问到的各种问题,因此如果你遇到问题的话,不妨现在这里找找,看看有没有已有的解决方案.
目前离完成还尚早,会不断更新...
Forge安装与开发环境配置
Forge安装与开发环境配置
ForgeGradle配置慢/配置失败
考虑使用FGOW.
不过Gradle本体下载(具体表现是第一次在cmd运行Gradle时显示形如"Downloading http://services.gradle.org/distributions/gradle-X.X-XXX.zip"的字样)的速度慢是无解的,目前国内唯一的Gradle本体的镜像只有bangbang93的BMCLAPI2提供了2.0版的镜像,启用方法是编辑forge目录下的gradle/wrapper/gradle-wrapper.properties,将其中的
distributionUrl=http\://services.gradle.org/distributions/gradle-2.0-bin.zip
distributionUrl=http\:///gradle-2.0-bin.zip
其他版本的Gradle目前还没有镜像,也许我有时间会往FMM上传个镜像...
在decompileMc或recompileMc时出错
这个一般是由于内存不足所导致,出错信息多包含有"java.lang.OutOfMemoryError: Java heap space"之类的.解决方法是用文本编辑器打开gradlew.bat(Windows下)或gradlew(OSX或Linux),在"set DEFAULT_JVM_OPTS="的后面加上-Xmx1000m,这个是手动设置最大1000M内存,通常来说是够用了,如果还不够的话可以继续调大.
更换Gradle位置
在Win7/8/8.1中,Gradle默认是把库下载在C:\Users\[用户名]\.gradle中,如果要修改默认位置的话,在环境变量的系统变量里,添加项GRADLE_USER_HOME,内容填新的位置.
在更换目录后,你可能需要输入gradlew.bat inti来重新修复一下.
Eclipse切换工作目录后是个空的项目
大概是配置没完成吧...在setupDecompoWorkspace后别忘了再执行eclipse任务,让Gradle为项目生成内容.
所谓的"Java HotSpot(TM) 64-Bit Server VM warning: Using incremental CMS is deprecated and will likely be removed in a future release"
很多人都说在启动游戏时遇到了这个错误结果进不去游戏,尽管我从来没遇到,但我相信所有的错误肯定都不是因为它的原因...它只是因为Java8将CMS回收器设定为"不推荐使用",而Forge的默认MC启动配置是使用CMS回收器所导致.这肯定不是导致出错的原因,然而由于某些奇妙的原因,这句话往往是在控制台的最后一句话输出,因此很多从不读文字的人误将它认为是Bug原因.
如果你是使用Forge1.7.2和Java8的话,那就把Forge升级到1.7.10或1.8,或者把Java降级到Java7.
在开发环境下无法启动服务器
看看控制台信息在闪退前最后几行有没有输出"You need to agree to the EULA...",有的话打开Forge目录下的eclipse/eula.txt,将eula=false改成eula=true.
中文目录/带有空格的目录
尽量避免目录中带有中文或空格,同样的尽量避免Mod文件的文件名中包含非Ascii字符(文件内含有非Ascii字符没关系,只要文件编码正确即可).
IDE项目缺少必备的库
正常情况下ForgeGradle在配置过程中会下载或生成(比如反混淆后的Minecraft)必须的库,但在少数情况下仍会发生"Project 'Minecraft' is missing required library"这样的缺库问题,主要是这三种情况引起:
正确下载或生成了库,但在生成IDE项目时没有正确加载库:这种情况下可以执行cleanEclipse/cleanIdea清理项目文件,然后再执行eclipse/idea重新生成IDE项目.还不行的话可以尝试手动指定库的位置...
生成库失败:forgeSrc/forgeBin和start这三个库是由ForgeGradle在配置项目时生成的,这种情况下就得考虑从头开始重新配置一遍了...
下载库失败:Gradle确实有如果下载库失败时就会静默地跳过然后在生成IDE项目时给你留个缺失库的特性...然而这在ForgeGradle不太可能出现,因为ForgeGradle在生成项目时的其中一步就是重新编译MC,如果缺库的话会立刻发现的.
启动时出现java.lang.reflect.InvocationTargetException
看一下下面的Caused by是不是"java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path",如果是的话则是JNI没有设定到正确的位置.
通常来讲ForgeGradle在配置时会自动设置项目引用的JNI库的位置,然而那东西你懂的...很多时候只是勉强JustWork(TM).可以尝试以下手动修复方式:
对于Eclipse,打开你的项目的项目属性,在Java Build Path - Libraries中,随便找一个库(比如forgeSrc),点开它前面的白箭头将它展开,双击Native Library Location,将目录设定到"C:/Users/[用户名]/.gradle/caches/minecraft/net/minecraft/minecraft_natives/[MC版本]",然后应该就可以了,如果你找不到那个目录的话,说明你配置失败了...重新配置一遍开发环境吧.
IDEA或Netbeans也同理,找各自的模块/项目配置,将一个库的JNI目录设到那个文件夹即可.
父类构造函数未定义等构造函数错误
使用/继承了错误的Block类,你在开发环境下能看到起码六种Block类,正确的Block类是net.minecraft.block包下的类.
砖块实时更新,实时处理砖块发生的变化
首先你要明白自己是在做什么,服务器的地图中你的砖块可能有少至几十块,多至几千块,如果你希望系统实时处理它们,你得知道服务器面临着多大的压力.
砖块有两种更新方式,被动更新和主动更新.
被动更新是指临近砖块发生变动时引发,比如当你调用World类的notifyBlocksOfNeighborChange方法可以让某点的上下左右前后6个方向的临近砖块进行被动更新,也可以调用notifyBlockOfNeighborChange来让某个点的砖块进行被动更新.被动更新调用的Block类方法是onNeighborBlockChange,用它你可以处理诸如红石信号变化,或者临近砖块的破坏等事件.
主动更新则是每隔一段时间固定引发一次.主动更新都是调用Block类的updateTick方法,但更新时间间隔有两种,一个是永久性随机更新,即间隔随机的一段时间时进行更新,启用随机更新的办法是在初始化砖块时调用setTickRandomly(true),随机更新适用于那些对更新频率要求不高的,比如农作物什么的;另一种更新则是一次性定时更新,通过调用World类的scheduleBlockUpdate或scheduleBlockUpdateWithPriority来让游戏在指定帧间隔后调用某点砖块的updateTick,在调用完之后,游戏就不会再调用了,除非你手动再申请一次,这种更新适合像按钮之类的会一次性定时发生变化的砖块;如果你在updateTick中申请定时更新的话,那这就变成永久性的固定间隔更新了,适用于像流体那种需要频繁更新的东西.
创建一种液体
Forge提供了一套流体系统,具体见,Wiki上难得一篇写的好的文章...我不觉得我能写的比它更清楚.
创建每面材质不同的砖块(即多材质砖块)
1.7:最简单的是像书架那样与方向无关的多面砖块,只要重写砖块类的getIcon,根据不同面(第一个参数)来返回不同材质,然后再重写registerBlockIcons来加载多个材质就行.比如:
public class BlockSkybox extends Block {
private IIcon[]
public BlockSkybox() {
super(Material.rock);
setBlockName(&SkyboxBlock&);
setCreativeTab(CreativeTabs.tabBlock);
//因为下面我们是手动加载材质,所以就不需要setBlockTextureName了
public IIcon getIcon(int face, int meta) {
return icons[face];
public void registerBlockIcons(IIconRegister iIconRegister) {
icons = new IIcon[6];
icons[0] = iIconRegister.registerIcon(&examplemod:csskybox_down&);
icons[1] = iIconRegister.registerIcon(&examplemod:csskybox_up&);
icons[2] = iIconRegister.registerIcon(&examplemod:csskybox_north&);
icons[3] = iIconRegister.registerIcon(&examplemod:csskybox_south&);
icons[4] = iIconRegister.registerIcon(&examplemod:csskybox_west&);
icons[5] = iIconRegister.registerIcon(&examplemod:csskybox_east&);
而如果要实现像熔炉或活塞那样有方向的多材质方块就有点复杂,首先是要判断砖块该朝向哪个方向,BlockPistonBase类的determineOrientation方法可以用来判断合适的朝向,返回的数值即砖块正向所在的面;然后是数值的存储和读取,这个可以用metadata来完成.下面给出一个不完善的例子,不完善的地方在稍后解释:
public class BlockKokoro extends BlockDirectional {
private IIcon forward, left, right, top,
public BlockKokoro() {
super(Material.iron);
setBlockName(&KokoroBlock&);
setCreativeTab(CreativeTabs.tabBlock);
public void onBlockAdded(World world, int x, int y, int z) { //通过setBlock放置的砖块
world.setBlockMetadataWithNotify(x, y, z, 2, 1 | 2); //一个更复杂的机制是判断附近哪个方向没有其他砖块,然后朝向那里.这里我们简单地让砖块朝北.
public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase entity, ItemStack itemStack) {
int meta = BlockPistonBase.determineOrientation(world, x, y, z, entity);
world.setBlockMetadataWithNotify(x, y, z, meta, 1 | 2); //1|2的来历见World类的setBlock
public IIcon getIcon(int face, int meta) {
switch (meta) {
case 0: //朝下
face = Facing.oppositeSide[face];
case 1: //朝上
switch (face)
case 2: //朝北
face = Facing.oppositeSide[face];
case 3: //朝南
switch (face)
case 4: //朝西
face = Facing.oppositeSide[face];
case 5: //朝东
switch (face)
public void registerBlockIcons(IIconRegister iIconRegister) {
forward = iIconRegister.registerIcon(&examplemod:kokoro_f&);
left = iIconRegister.registerIcon(&examplemod:kokoro_l&);
right = iIconRegister.registerIcon(&examplemod:kokoro_r&);
top = iIconRegister.registerIcon(&examplemod:kokoro_t&);
other = iIconRegister.registerIcon(&examplemod:kokoro_o&);
这个跟上一个相比主要是多了个onBlockPlacedBy和onBlockAdded,用来判断放置砖块时的朝向,然后在getIcon里根据渲染面和砖块朝向的关系来决定材质.这个方案的不完善之处在于材质不会旋转,当正面朝上或朝下时问题尤为明显,在1.7下解决方案恐怕只有将侧面材质设计为与方向无关的对称图形,或者为侧面材质额外制作其他的版本,比如左旋和右旋版本.
待补充,如果我过了很久都忘了写的话尽管spam我!
设定砖块在地图上的颜色
砖块在地图上的颜色由它的材质决定,材质的构造函数中的参数就是地图颜色,遗憾的是游戏似乎不支持自定义颜色...
1.8的BlockState和Property
这部分其实应该放在砖块章节,然而由于教程目前尚未全面更新到1.8,因此现在这里写一下.
在1.8之前,我们如果想在砖块中存储额外信息的话只能使用4位的Metadata.而从1.8开始,Minecraft提供了BlockState系统.
BlockState系统的总体思想是提供一种更好地表达一个砖块的状态的方案,这个方案既要能直白地表达要存储的状态;又要足够的小,不至于让内存或硬盘爆掉;同时也要足够的快.最终产物就是BlockState.
原版BlockState系统(Forge对它做了扩展,这个以后有时间再写)主要由这三部分组成:
BlockState类:描述一种砖块(即一个Block类的实例)所有可能的状态.
IBlockState接口:代表一种状态.
IProperty接口:代表状态中的一种属性.
我们以1.8的石头砖块的BlockStone类为例来解释各部分的作用和使用方法.
首先它先声明了一种属性:
public static final PropertyEnum VARIANT = PropertyEnum.create(&variant&, BlockStone.EnumType.class);
从代码中可以很直观地看到VARIANT是一种枚举类型的属性,PropertyEnum继承自PropertyHelper,而PropertyHelper则实现了IProperty.Minecraft默认提供了这三种属性类型的实现:连续整数(PropertyInteger),枚举(PropertyEnum)和布尔值(PropertyBool),其中枚举还有一个专门用于描述方向的封装PropertyDirection.
然后我们要声明BlockState,这部分的代码在稍微靠下的地方:
protected BlockState createBlockState()
return new BlockState(this, new IProperty[] {VARIANT});
它重写了基类的createBlockState,声明了一个拥有VARIANT属性的BlockState,它的构造函数会自动计算所有属性能组成的全部状态,用数学的说法是求它们的笛卡尔积,比如如果一个BlockState有一个取值范围在0~2的连续整数属性和一个布尔值属性,那么它就有6种状态,分别是{0 False, 0 True, 1 False, 1 True, 2 False, 2 True}.
我们之前提到IBlockState接口的实例代表一种状态,首先我们先说明如何修改状态,修改状态是通过IBlockState的withProperty方法,这个方法的参数是给定一个待更新属性和它的待更新值,返回值是更新后的状态,还是以刚才的为例,假如那个整数属性叫INT,那么要将一个值为{0 False}的状态改成{2 False}的话,代码是:
IBlockState state = oldState.withProperty(INT, Integer.valueOf(2)); //oldState为旧状态,state为修改值后的新状态
需要注意的是,withProperty不会对旧状态产生任何影响,上述代码在运行完后oldState不会有任何改变,因此你永远无需担心调用withProperty会破坏旧状态,你可能会认为Minecraft默认的IBlockState实现采用的是不变量(Immutable)设计,即每次修改值时始终返回一个新实例而不会改变原实例,这并不是完全正确的,实际上是BlockState已经预先生成好了所有可能的状态,每次调用withProperty时只是根据一个状态转移表查找到应转移到的状态,然后返回那个状态的实例.
通过withProperty,我们可以将任何一个已有状态修改成我们想要的状态,但是我们该如何获取"第一个"状态,也就是如何凭空获取一个IBlockState实例? BlockState提供了getBaseState方法可以获取一个空的状态,然而这个空状态的值是不确定的,每次手工赋值必然很麻烦,因此Block类允许配置一个默认状态,设置默认状态的办法是调用Block类的setDefaultState,以BlockStone为例:
this.setDefaultState(this.blockState.getBaseState().withProperty(VARIANT, BlockStone.EnumType.STONE));
这个代码是设置该砖块的默认状态为{STONE},以后如果要获取默认状态,直接调用Block类的getDefaultState方法即可.
最后我们要考虑的是如何保存状态,现在我们又要和老朋友Metadata打招呼了,MC1.8仍然使用Metadata系统,并且也依然受最多16种取值的限制,所以说你的BlockState虽然最多可能有超过16种状态,但在保存时必须只保留最关键的属性,将它简化到16种以下,比如说如果你设计了一种管道砖块,它有2种属性,一个有16种取值的用于标识管线类型的枚举属性,和一个范围在0~63用于标识管道与周围连接情况的整数属性(如果你好奇0~63是怎么来的话...使用6个二进制位来标识这个砖块与周围6个方向的连通情况),显然它们的笛卡尔积有多达1024种情况,然而我们可以只保存标识管线类型的那个属性,标识连接情况的属性可以在运行时通过判断周围砖块的类型来推算出来.
保存、读取和计(bu)算(wan)非关键属性的方法以及它们的使用范例分别是:
public int getMetaFromState(IBlockState state)
//返回值为该状态的Metadata的值,不需要的话就返回0或者干脆不重写这个方法
return ((EnumPipeType)state.getValue(pipeType)).getMetadata(); //模仿BlockStone的写法
public IBlockState getStateFromMeta(int meta)
//返回值为该Metadata对应的状态,不需要的话就返回getDefaultState或者干脆不重写这个方法
return getDefaultState().withProperty(pipeType, EnumPipeType.byMetadata(meta)); //模仿BlockStone的写法
public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos)
//返回值为补全了非关键属性的状态,如果不需要补全数据的话,直接&&即可;或者干脆不重写这个方法.
int i = 0;
i |= (worldIn.getBlockState(pos.up()
).getBlock() == this ? 1 : 0) && EnumFacing.UP
.getIndex();
i |= (worldIn.getBlockState(pos.down() ).getBlock() == this ? 1 : 0) && EnumFacing.DOWN .getIndex();
i |= (worldIn.getBlockState(pos.east() ).getBlock() == this ? 1 : 0) && EnumFacing.EAST .getIndex();
i |= (worldIn.getBlockState(pos.west() ).getBlock() == this ? 1 : 0) && EnumFacing.WEST .getIndex();
i |= (worldIn.getBlockState(pos.north()).getBlock() == this ? 1 : 0) && EnumFacing.NORTH.getIndex();
i |= (worldIn.getBlockState(pos.south()).getBlock() == this ? 1 : 0) && EnumFacing.SOUTH.getIndex();
return state.withProperty(linkingState, Integer.valueOf(i));
原版BlockState系统的内容便就此结束了,如果你问BlockState系统的作用的话,那就是它创造了数据驱动渲染的可能,数据驱动就是指通过外部数据来决定程序运作流程,而不是通过硬编码的代码,比如BlockModelShapes类中的这行代码:
this.registerBlockWithStateMapper(Blocks.stone, (new StateMap.Builder()).setProperty(BlockStone.VARIANT).build());
这样便建立了砖块状态与渲染的映射关系,游戏会根据VARIANT属性的值从assets/minecraft/bloackstates中选择合适的数据文件用于渲染.
实体的额外数据与持久化
调用实体的getEntityData方法可以获取一个Forge提供的专门用于存储自定义信息的NBT组件,服务器会自动完成它的保存和读取.然而在多人游戏中这些数据不会被服务器同步给客户端,因此你可能需要借助DataWatcher或自定义封包等方式来实现客户端到服务器的数据同步,这个问题将在网络章节中讨论.
此外,对于玩家实体来说还有一种方式来实现持久化,就是监听PlayerEvent.SaveToFile事件和PlayerEvent.LoadFromFile事件,这两个是在服务器保存和读取一个玩家的信息时发生,这两个事件均提供了名叫getPlayerFile的方法,通过这个方法你可以让Forge为你推荐一个指定后缀名的存储文件位置(并不会立刻创建文件),举例:
@SubscribeEvent
public void onPlayerLoad(PlayerEvent.LoadFromFile event) {
File file = event.getPlayerFile(&nvm&);
Date registerT
if(!file.exists())
ObjectOutputStream stream =
registerTime = new Date();
stream = new ObjectOutputStream(new FileOutputStream(file));
stream.writeObject(registerTime);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(stream != null)
try { stream.close(); } catch (Exception e2) {}
ObjectInputStream stream =
stream = new ObjectInputStream(new FileInputStream(file));
registerTime = (Date)stream.readObject();
} catch (Exception e) {
e.printStackTrace();
registerTime = new Date();
} finally {
if(stream != null)
try { stream.close(); } catch (Exception e2) {}
//把registerTime存在某处,比如一个map中...
//saves\New World\playerdata\a498be5d--a17c-b0.nvm
这段代码用来记录玩家首次登陆服务器的"注册时间"(虽然这用NBT能更好地完成).游戏会在玩家进入游戏时检查存档中的playerdata目录下有没有名为"[玩家的uuid].nvm"的文件,如果没有的话,则会创建一个并写入玩家的登陆时间;有的话则会读取.
这段代码没有考虑文件写入的原子性,如果文件较大,或者纯粹是运气不好的话,可能在你向磁盘中写入文件时恰好赶上服务器崩溃、电源掉电、机柜被维修工当成空调拉走等各种让程序中断的情况,如果此时正巧你在向硬盘中写入数据的话,文件就损坏了,可以想象当重新上线的玩家看到他的数据一夜回到解放前时会有多么愤怒,因此如果你要保存的文件较大的话,应当考虑先输出到临时文件当中,在输出完毕后删掉旧文件并重命名临时文件.
如果你是个超负责任的人的话,你会发现这里还有个小问题,就是删掉旧文件和重命名临时文件这两个操作也不是原子性的...可能旧文件删掉后临时文件尚未改名系统便崩溃了,对于这种情况有3种办法:
1.自验证完整性:在保存的文件中加入能够验证完整性的内容,比如在结尾加入个特殊字符什么的,如果在载入文件时发现有临时文件存在的话,就验证临时文件是否完整,完整的话就使用临时文件的内容,另外别忘了顺便把之前未完成的工作完成了.
2.旧文件备份:这个的思路是在完成临时文件的写入后,不立刻删掉旧文件,而是将旧文件改名为一个备份文件,然后将临时文件改名为正常文件.当读取文件时,如果正常文件不存在的话,就去读备份文件.
3.原子性替换:大部分符合POSIX标准的操作系统都支持原子性文件替换,Windows号称部分符合POSIX标准,遗憾的是这不是其中之一.不过Java7提供了跨平台的原子性文件替换方法,比如:
Files.move(tempFile.toPath(), oldFile.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
然而这个是Java7的功能,MC要求的最低版本是Java6,虽然我不觉得现在还有土鳖在使用Java6,但谁知道会不会真有人在用呢...而且这个方法有潜在的失败可能,文档中明确提到如果原子操作不可行(比如在Windows中进行跨盘复制时)的话会立刻抛出异常.
如果不考虑方法3的话,方法1和方法2的区别在于1是在发生问题时尽量使用新文件,2是在发生问题时尽量使用旧文件;1需要特别设计的文件格式,而2则不需要.
世界的额外数据与持久化
WorldInfo类的setAdditionalProperties和getAdditionalProperty方法可以分配或获取一个NBT节点,这些数据会随着该WorldInfo所属的World被一并保存或读取.显然在多人游戏中这些数据不会被服务器自动同步给客户端,如果你需要的话,可能得通过自定义封包来手动同步数据.
获取WorldInfo的办法是调用World类的getWorldInfo.
此外,GameRuler类也可以用来保存数据,然而它只能存储字符串.
获取一条线段上最近的砖块
World类提供了rayTraceBlocks方法用于查找一条有向线段中第一个接触线段的砖块,它有如下3种重载(其中一个在1.7.10中叫func_147447_a):
rayTraceBlocks(Vec3 start, Vec3 end, boolean includeLiquidAndFire, boolean ignoreBlockWithoutBoundingBox, boolean returnLastUncollidableBlock)
start和end为线段的起点和终点;includeLiquidAndFire为是否包括火焰和液体;ignoreBlockWithoutBoundingBox为是否忽略无碰撞(也就是没有碰撞箱)的砖块;returnLastUncollidableBlock为是否一定要返回一个非null结果,如果此参数为true的话,即使未找到结果也会返回最后一个无碰撞砖块的位置,同时返回结果(一个MovingObjectPosition实例)中的typeOfHit字段会被标记为MISS,如果为false,或者连无碰撞砖块都找不到的话,则会返回null.
rayTraceBlocks(Vec3 start, Vec3 end, boolean includeLiquidAndFire)
重载上一个,ignoreBlockWithoutBoundingBox和returnLastUncollidableBlock都为false.
rayTraceBlocks(Vec3 start, Vec3 end)
重载上一个,includeLiquidAndFire为false.
生成粒子特效
生成粒子特效可以用World类的spawnParticle,它的7个参数分别是"粒子类型"、粒子的初始位置和初始速度矢量,可用的粒子类型详见中的Particle name一项.
此外,只有在客户端调用spawnParticle才会有效果,在服务器端调用会被静默地忽视掉.
自定义粒子特效
如果现有的粒子特效不够用的话,可以自行创建一个EntityFX的子类,然后在客户端的代理器(Proxy)通过Minecraft.getMinecraft().effectRenderer.addEffect来添加粒子特效.
需要注意的有两个问题,一个是添加自定义粒子特效必须在客户端进行,因为服务器就压根没EffectRenderer和EntityFX这两个东西...第二个是在添加时必须根据待添加的世界的isRemote方法判断是否为客户端世界,即使你的代码已经是在客户端代理器中也必须进行这个判断,并且只在客户端世界中添加特效,如果不这样做的话,你会有一定几率在单人游戏时遇到一个奇特的竞态条件,具体表现形式为在创建特效时游戏会有一定几率弹出,错误原因是随机的一个实体的moveEntity方法在服务器线程中出现了一个NullPointerException,出现位置是在一个"for (int i = 0; i < list.size(); ++i)"的循环体中,其中list.size()为0...看起来像是服务器线程和客户端线程在同时操作一个列表时发生了冲突.
这里给出一个简单的示例 - 砍怪时飙血.
首先是在客户端代理器中监听实体遭受攻击事件,然后创建相应的粒子特效:
[code lang="java"]
@SubscribeEvent
public void onEntityAttacked(LivingAttackEvent event) {
Entity attacking = event.source.getSourceOfDamage(); //attacking为攻击者
if(attacking == null || !attacking.worldObj.isRemote) //对于攻击者不是实体,或者该世界不为客户端世界的情况,直接返回
EntityLivingBase attacked = event.entityL //attacked为受害者
for(int i = 0; i & 10; i++)
Minecraft.getMinecraft().effectRenderer.addEffect(
//添加一个待会制作的EntityBlood实体,它的参数是游戏世界,3个double表示的粒子位置,以及1个Vec3表示的方向矢量
new EntityBlood(attacked.worldObj,
attacked.posX,
attacked.posY + attacked.getEyeHeight(),
attacked.posZ,
Vec3.createVectorHelper(attacking.posX - attacked.posX, //根据攻击者和被攻击者的位置计算出血喷出的方向
attacking.posY - attacked.posY - attacked.getEyeHeight(),
attacking.posZ - attacked.posZ).normalize())); //方向矢量最好是单位长度,因此做一次规格化
然后要制作EntityBlood类,它的代码并不复杂.
[code lang="java"]
@SideOnly(Side.CLIENT)
public class EntityBlood extends EntityFX {
private final float PI3 = (float)(Math.PI / 3.0);
private final float PI6 = (float)(Math.PI / 6.0);
public EntityBlood(World world, double posX, double posY, double posZ, Vec3 direction) {
super(world, posX, posY, posZ);
double speed = rand.nextDouble() * 0.3d; //设定速度为0.0~0.3范围内的随机数
direction.rotateAroundY((rand.nextFloat() - 0.5f) * PI3); //让喷血方向带有一定的随机性
direction.rotateAroundX((rand.nextFloat() - 0.5f) * PI6);
direction.rotateAroundZ((rand.nextFloat() - 0.5f) * PI6);
setVelocity(direction.xCoord * speed, direction.yCoord * speed, direction.zCoord * speed); //根据方向矢量设定速度矢量
setSize(0.1f, 0.1f); //设定粒子的碰撞尺寸,如果不想要碰撞的话不妨试试noClip = true ?
this.particleMaxAge = (int)(10f / (this.rand.nextFloat() * 0.75f + 0.25f)); //设定粒子的寿命,单位为tick
this.particleGravity = 0.3f; //受重力加速度影响的系数,1.0为每tick Y方向速度减-0.04 (不考虑空气阻力的话)
this.particleGreen = 0.0f; //RGB三颜色中的G系数,默认全为1.0,即白色/显示纹理正常颜色
this.particleBlue = 0.0f; //将G和B全改为0的话,那就是红色/纹理&偏&红
this.particleAlpha = 1.0f; //不透明度,默认就是1.0...
this.particleScale = 3.0f; //尺寸系数,默认是1.0~2.0之间的随机数
关于粒子纹理图可以见,默认的粒子纹理是左上角的那个小方点.想修改的话可以用setParticleTextureIndex来设定所使用的部分,计算方法是把图片分成16x16共256份,然后按照从左往右,从上往下的顺序数,第一行第一列(也就是默认部分)为0,第一行第二列为1,第二行第一列为16.愤怒的村民脸为83.
想让血液纹理为默认的8种爆炸烟雾之一的话,只要在构造函数中添加:
setParticleTextureIndex(rand.nextInt(8));
如果想要自定义纹理的话倒也不是很麻烦,但效率很低,由于默认的EffectRenderer的限制,要在每次渲染时重启Tessellator,然后重新绑定纹理,完成渲染后还要再次重启Tessellator并绑定回原纹理.这里给出一个效率不是很高的实现:
@SideOnly(Side.CLIENT)
public class EntityBlood extends EntityFX {
private static TextureManager textureManager = Minecraft.getMinecraft().renderE
//值得注意的是,这里ResourceLocation必须写完整地址,不能像Block或Item中那样简写为&examplemod:splatt&了...
private static ResourceLocation texture = new ResourceLocation(&examplemod:textures/particles/splatt.png&);
private final float PI3 = (float)(Math.PI / 3.0);
private final float PI6 = (float)(Math.PI / 6.0);
public EntityBlood(World world, double posX, double posY, double posZ, Vec3 direction) {
super(world, posX, posY, posZ);
double speed = rand.nextDouble() * 0.3d; //设定速度为0.0~0.3范围内的随机数
direction.rotateAroundY((rand.nextFloat() - 0.5f) * PI3); //让喷血方向带有一定的随机性
direction.rotateAroundX((rand.nextFloat() - 0.5f) * PI6);
direction.rotateAroundZ((rand.nextFloat() - 0.5f) * PI6);
setVelocity(direction.xCoord * speed, direction.yCoord * speed, direction.zCoord * speed); //根据方向矢量设定速度矢量
setSize(0.1f, 0.1f); //设定粒子的碰撞尺寸,如果不想要碰撞的话不妨试试noClip = true ?
this.particleMaxAge = (int)(10f / (this.rand.nextFloat() * 0.75f + 0.25f)); //设定粒子的寿命,单位为tick
this.particleGravity = 0.3f; //受重力加速度影响的系数,1.0为每tick Y方向速度减-0.04 (不考虑空气阻力的话)
this.particleGreen = 0.0f; //RGB三颜色中的G系数,默认全为1.0,即白色/显示纹理正常颜色
this.particleBlue = 0.0f; //将G和B全改为0的话,那就是红色/纹理&偏&红
this.particleAlpha = 1.0f; //不透明度,默认就是1.0...
this.particleScale = 3.0f; //尺寸系数,默认是1.0~2.0之间的随机数
public int getFXLayer() {
//EffectRenderer根据使用的纹理图不同将粒子分为了3类(层),0类使用粒子纹理图,1类使用砖块纹理图,2类使用物品纹理图.
//这里我们跟2类共用一层倒也没什么玄学,纯粹是因为物品纹理图(TextureMap.locationItemsTexture)是public,而粒子纹理图是private...
public void renderParticle(Tessellator tessellator, float delta, float rotationX, float rotationXZ,
float rotationZ, float rotationYZ, float rotationXY) {
tessellator.draw(); //Tessellator一次渲染只能使用一种纹理,因此要重启并重新绑定
tessellator.startDrawingQuads();
textureManager.bindTexture(texture);
float f6 = 0.0f; //这4个参数是用来控制UV的,如果你是一个粒子对应一整个纹理的话,那就保留这个设定就行了
float f7 = 1.0f;
float f8 = 0.0f;
float f9 = 1.0f;
float scale = 0.1F * this.particleS
float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)delta - interpPosX);
float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)delta - interpPosY);
float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)delta - interpPosZ);
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha);
tessellator.addVertexWithUV((double)(f11 - rotationX * scale - rotationYZ * scale), (double)(f12 - rotationXZ * scale), (double)(f13 - rotationZ * scale - rotationXY * scale), (double)f7, (double)f9);
tessellator.addVertexWithUV((double)(f11 - rotationX * scale + rotationYZ * scale), (double)(f12 + rotationXZ * scale), (double)(f13 - rotationZ * scale + rotationXY * scale), (double)f7, (double)f8);
tessellator.addVertexWithUV((double)(f11 + rotationX * scale + rotationYZ * scale), (double)(f12 + rotationXZ * scale), (double)(f13 + rotationZ * scale + rotationXY * scale), (double)f6, (double)f8);
tessellator.addVertexWithUV((double)(f11 + rotationX * scale - rotationYZ * scale), (double)(f12 - rotationXZ * scale), (double)(f13 + rotationZ * scale - rotationXY * scale), (double)f6, (double)f9);
tessellator.draw();
tessellator.startDrawingQuads();
textureManager.bindTexture(TextureMap.locationItemsTexture); //绑定回物品纹理图
这里的纹理我放在了"assets/examplemod/textures/particles"中,纹理我使用的是一张HL1的喷漆图,希望G胖不会来爆我菊花吧 233333
Gabe:(此处应用古朗特的语气)Too late! YOU ARE DEAD!
不管怎么说,效果还是很好的,虽然说是效率有问题,但对于几十甚至几百个粒子来说,还是足以力保60fps大关的,如果你真的追求高性能自定义粒子渲染的话,可以不妨自己写一个粒子渲染器,EffectRenderer的代码并不复杂,RenderWorldLastEvent也可以作为自定义渲染器启动渲染的触发事件.
删除已有的合成与冶炼配方
所有的合成配方都储存在CraftingManager的recipes字段中,你可以通过调用CraftingManager.getInstance().getRecipeList()来获取它,它储存有所有已注册的合成配方,你可以遍历这个列表然后找出指定的合成并删掉它,这里有一个现成的已封装好的代码,以及使用范例:
* 移除一个合成配方,此版本仅对比合成的输出物,而且输出物的类型,数量和itemDamage(如果配方指定了itemDamage的话)必须全部匹配才会执行删除操作.
* @param output 输出物的物品栈
* @return 是否找到并移除了一个配方
public boolean removeRecipe(ItemStack output) {
for(Iterator&Object& iterator = CraftingManager.getInstance().getRecipeList().iterator(); iterator.hasNext();)
IRecipe recipe = (IRecipe)iterator.next();
ItemStack recipeOutput = recipe.getRecipeOutput();
if(OreDictionary.itemMatches(recipeOutput, output, false) && recipeOutput.stackSize == output.stackSize)
iterator.remove();
* 移除一个合成配方,此版本会详细对比合成的输入物,并且考虑输入物的类型和itemDamage(如果配方指定了itemDamage的话).
* @param input 见removeRecipe(ItemStack[] input, ItemStack output)
* @return 是否找到并移除了一个配方
public boolean removeRecipe(ItemStack[] input) {
return removeRecipe(input, null);
* 移除一个合成配方,此版本会详细对比合成的输入物,并且考虑输入物的类型和itemDamage(如果配方指定了itemDamage的话).
* 此外,还可以指定一个输出物用于筛选配方提高效率,输出物只考虑类型,不考虑数量和itemDamage.
* @param input 输入物组成的合成公式,需要严格按照添加合成配方时的样式进行,比如火炬是长度为2的数组,里面是1个煤和1个木棍;
工具台是长度为4的数组,里面是4个木头;告示牌是长度为9的数组,里面是6个木头,1个null,1个木棍,然后再跟1个null.
* @param output 输出物,用于预剔除来提高效率,可选参数,可以是null - 不进行预剔除.
* @return 是否找到并移除了一个配方
public boolean removeRecipe(ItemStack[] input, ItemStack output) {
dirtygoto: for(Iterator&Object& iterator = CraftingManager.getInstance().getRecipeList().iterator(); iterator.hasNext();)
IRecipe recipe = (IRecipe)iterator.next();
ItemStack recipeOutput = recipe.getRecipeOutput();
if(recipeOutput == null || (output != null && recipeOutput.getItem() != output.getItem()))
Object[] targetS
if(recipe instanceof ShapedRecipes)
targetStacks = ((ShapedRecipes)recipe).recipeI
else if(recipe instanceof ShapedOreRecipe)
targetStacks = ((ShapedOreRecipe)recipe).getInput();
else if(recipe instanceof ShapelessRecipes)
targetStacks = ((ShapelessRecipes)recipe).recipeItems.toArray();
else if(recipe instanceof ShapelessOreRecipe)
targetStacks = ((ShapelessOreRecipe)recipe).getInput().toArray();
if(targetStacks.length == input.length)
for(int i = 0; i & targetStacks. i++)
if(targetStacks[i] instanceof ItemStack)
if(!OreDictionary.itemMatches((ItemStack)targetStacks[i], input[i], false))
else if(targetStacks[i] instanceof ArrayList)
boolean match =
ArrayList list = (ArrayList)targetStacks[i];
for(Object object : list)
if(OreDictionary.itemMatches((ItemStack)object, input[i], false))
if(!match)
iterator.remove();
@EventHandler
public void postInit(FMLPostInitializationEvent event) {
//删除工具台
ItemStack itemStack = new ItemStack(Blocks.crafting_table);
removeRecipe(itemStack);
//删除告示牌
ItemStack plank = new ItemStack(Blocks.planks);
ItemStack stick = new ItemStack(Items.stick);
ItemStack sign = new ItemStack(Items.sign);
ItemStack[] itemStacks = new ItemStack[] {
plank, plank, plank,
plank, plank, plank,
stick, null
removeRecipe(itemStacks, sign);
//删除末地眼
ItemStack enderPearl = new ItemStack(Items.ender_pearl);
ItemStack powerDaze = new ItemStack(Items.blaze_powder);
ItemStack enderEye = new ItemStack(Items.ender_eye);
itemStacks = new ItemStack[] {enderPearl, powerDaze};
removeRecipe(itemStacks, enderEye);
//删除火炬 - 火炬有2种合成,因此要删两遍
ItemStack torch = new ItemStack(Blocks.torch);
itemStacks = new ItemStack[] {new ItemStack(Items.coal, 1, 0), stick}; //使用煤炭的合成
removeRecipe(itemStacks, torch);
itemStacks = new ItemStack[] {new ItemStack(Items.coal, 1, 1), stick}; //使用焦煤的合成
removeRecipe(itemStacks, torch);
在GUI界面中渲染玩家
如果是在游戏中的话这倒不是难事,GuiInventory类的drawEntityOnScreen就可以绘制一个生物实体到屏幕上.但如果不在游戏中,比如在主菜单时,想绘制一个玩家的话就不容易了,因为这时没有游戏世界,也没有玩家尸体.但也并非不可实现.这里给出一个参考示例,一个开箱即用的玩家渲染器.
16-1-25更新:修复了导致游戏内玩家模型渲染错误的bug...两个版本的变更代码均为static块内的内容.
* 在屏幕中以正交投影渲染一个玩家,建议在DrawScreenEvent.Post事件或界面类的drawScreen中进行.
@SideOnly(Side.CLIENT)
public class ScreenPlayerRenderer {
//玩家模型
private static final ModelBiped steveM
public static final ResourceLocation defaultSteveTexture = new ResourceLocation(&textures/entity/steve.png&);
private static final Map&String, ResourceLocation& skinCache = new ConcurrentHashMap&String, ResourceLocation&();
private ModelBiped model = steveM
private boolean isAlex =
private float headPitch = 0f, headYaw = 0f;
private float frame = 0f, walk = 0f;;
steveModel = new ModelBiped();
* 设置皮肤贴图为默认贴图
public void setSkin() {
Minecraft.getMinecraft().renderEngine.bindTexture(defaultSteveTexture);
* 根据玩家名称自动从网上获取贴图,在贴图读取完毕前,玩家贴图会显示为默认贴图.
public void setSkin(String name) {
ResourceLocation resourceLocation = skinCache.get(name);
if(resourceLocation == null)
//此处为net.minecraft.util下的StringUtils
resourceLocation = new ResourceLocation(&skins/& + StringUtils.stripControlCodes(name));
AbstractClientPlayer.getDownloadImageSkin(resourceLocation, name);
skinCache.put(name, resourceLocation);
Minecraft.getMinecraft().renderEngine.bindTexture(resourceLocation);
* 设置皮肤贴图为一个已有的贴图
public void setSkin(ResourceLocation resourceLocation) {
Minecraft.getMinecraft().renderEngine.bindTexture(resourceLocation);
* 设置头部的朝向,默认是平视正前方
* @param pitch 负数朝上看,正数朝下看,单位为角度制
* @param yaw 负数朝左看,正数朝右看,单位为角度制
public void setHeadLook(float pitch, float yaw) {
headPitch =
* 手动设定当前帧数,通常来说是不需要的
public void setFrame(float frameTime) {
frame = frameT
* 设定人物行走动作,0.0为原地站立不动,1.0为正常行走
* @param speed
public void setWalk(float speed) {
public void render(int posX, int posY, float pitch, float yaw, float roll, float size) {
render(posX, posY, pitch, yaw, roll, size, 0f);
* 在屏幕上渲染玩家.
* @param posX 屏幕坐标上的原点位置的X坐标
* @param posY 屏幕坐标上的原点位置的Y坐标
* @param pitch 绕X轴旋转角度,单位为角度制.负数上仰,正数下俯.注意坐标原点是在玩家的脚下而不是正中心.
* @param yaw 绕Y轴旋转的角度,单位为角度制.负数向左,正数向右.
* @param roll 绕Z轴旋转的角度,单位为角度制
* @param size 缩放倍数,0.0的话就看不见了...建议在2.0~4.0之间.
* @param delta 递增帧数的参数,用于维持播放玩家的行走动作和手臂自然摆动等,通过控制数值大小可以控制动作速度.
public void render(int posX, int posY, float pitch, float yaw, float roll, float size, float delta) {
model.setRotationAngles(frame * walk, walk, frame, headYaw, headPitch, 0f, null);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glPushMatrix();
//这里是个很玄学的地方,需要把深度测试的方式颠倒一下才能正常渲染,我承认我也不知道这为什么...但它JustWork(TM)!
//不这样做的话,渲染出来的效果会跟没开深度测试似的
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthFunc(GL11.GL_GREATER);
GL11.glTranslatef(posX, posY, -500.0f);
GL11.glScalef(size, size, size);
GL11.glRotatef(roll, 0.0f, 0.0f, 1.0f);
GL11.glRotatef(yaw, 0.0f, 1.0f, 0.0f);
GL11.glRotatef(pitch, 1.0f, 0.0f, 0.0f);
GL11.glColor3f(1.0f, 1.0f, 1.0f);
model.bipedHead.render(1.0f);
model.bipedBody.render(1.0f);
model.bipedRightArm.render(1.0f);
model.bipedLeftArm.render(1.0f);
model.bipedRightLeg.render(1.0f);
model.bipedLeftLeg.render(1.0f);
model.bipedHeadwear.render(1.0f);
GL11.glDepthFunc(GL11.GL_LEQUAL);;
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glPopMatrix();
GL11.glDisable(GL11.GL_COLOR_MATERIAL);
1.8版有个小小的缺陷,就是无法自动判断玩家使用的皮肤贴图类型是Steve还是Alex.
* 在屏幕中以正交投影渲染一个玩家,建议在DrawScreenEvent.Post事件或界面类的drawScreen中进行.
@SideOnly(Side.CLIENT)
public class ScreenPlayerRenderer {
//Steve和Alex的模型
private static final ModelBiped steveModel, alexM
public static final ResourceLocation defaultSteveTexture = new ResourceLocation(&textures/entity/steve.png&),
defaultAlexTexture = new ResourceLocation(&textures/entity/alex.png&);
private static final Map&String, ResourceLocation& skinCache = new ConcurrentHashMap&String, ResourceLocation&();
private ModelBiped model = steveM
private boolean isAlex =
private float headPitch = 0f, headYaw = 0f;
private float frame = 0f, walk = 0f;;
steveModel = new ModelBiped();
alexModel = new ModelBiped();
copyModel(new ModelPlayer(0f, false), steveModel);
copyModel(new ModelPlayer(0f, true), alexModel);
* 1.8中玩家模型是ModelPlayer类,它有个缺点是setRotationAngles的实体参数必须为非null
* (ModelBiped类的setRotationAngles的实体参数就可以是null) 故此这里创建一个替代品并把必要的参数复制过去
private static void copyModel(ModelPlayer src, ModelBiped dest) {
dest.bipedBody = src.bipedB
dest.bipedHead = src.bipedH
dest.bipedHeadwear = src.bipedH
dest.bipedLeftArm = src.bipedLeftA
dest.bipedRightArm = src.bipedRightA
dest.bipedLeftLeg = src.bipedLeftL
dest.bipedRightLeg = src.bipedRightL
* 设置皮肤类型是否为Alex
public void setAlex(boolean isAlex) {
this.isAlex = isA
model = isAlex ? alexModel : steveM
* 设置皮肤贴图为默认贴图
public void setSkin() {
Minecraft.getMinecraft().renderEngine.bindTexture(isAlex ? defaultAlexTexture : defaultSteveTexture);
* 根据玩家名称自动从网上获取贴图,在贴图读取完毕前,玩家贴图会显示为默认贴图.
public void setSkin(String name) {
ResourceLocation resourceLocation = skinCache.get(name);
if(resourceLocation == null)
//此处为net.minecraft.util下的StringUtils
resourceLocation = new ResourceLocation(&skins/& + StringUtils.stripControlCodes(name));
AbstractClientPlayer.getDownloadImageSkin(resourceLocation, name);
skinCache.put(name, resourceLocation);
Minecraft.getMinecraft().renderEngine.bindTexture(resourceLocation);
* 设置皮肤贴图为一个已有的贴图
public void setSkin(ResourceLocation resourceLocation) {
Minecraft.getMinecraft().renderEngine.bindTexture(resourceLocation);
* 设置头部的朝向,默认是平视正前方
* @param pitch 负数朝上看,正数朝下看,单位为角度制
* @param yaw 负数朝左看,正数朝右看,单位为角度制
public void setHeadLook(float pitch, float yaw) {
headPitch =
* 手动设定当前帧数,通常来说是不需要的
public void setFrame(float frameTime) {
frame = frameT
* 设定人物行走动作,0.0为原地站立不动,1.0为正常行走
* @param speed
public void setWalk(float speed) {
public void render(int posX, int posY, float pitch, float yaw, float roll, float size) {
render(posX, posY, pitch, yaw, roll, size, 0f);
* 在屏幕上渲染玩家.
* @param posX 屏幕坐标上的原点位置的X坐标
* @param posY 屏幕坐标上的原点位置的Y坐标
* @param pitch 绕X轴旋转角度,单位为角度制.负数上仰,正数下俯.注意坐标原点是在玩家的脚下而不是正中心.
* @param yaw 绕Y轴旋转的角度,单位为角度制.负数向左,正数向右.
* @param roll 绕Z轴旋转的角度,单位为角度制
* @param size 缩放倍数,0.0的话就看不见了...建议在2.0~4.0之间.
* @param delta 递增帧数的参数,用于维持播放玩家的行走动作和手臂自然摆动等,通过控制数值大小可以控制动作速度.
public void render(int posX, int posY, float pitch, float yaw, float roll, float size, float delta) {
model.setRotationAngles(frame * walk, walk, frame, headYaw, headPitch, 0f, null);
GlStateManager.enableColorMaterial();
GlStateManager.pushMatrix();
//这里是个很玄学的地方,需要把深度测试的方式颠倒一下才能正常渲染,我承认我也不知道这为什么...但它JustWork(TM)!
//不这样做的话,渲染出来的效果会跟没开深度测试似的
GlStateManager.depthFunc(GL11.GL_GREATER);
GlStateManager.translate(posX, posY, -500.0f); //正常情况下深度应该是500,但由于这里的深度测试被颠倒了,因此要取-500才会渲染出来
GlStateManager.scale(size, size, size);
GlStateManager.rotate(roll, 0.0f, 0.0f, 1.0f);
GlStateManager.rotate(yaw, 0.0f, 1.0f, 0.0f);
GlStateManager.rotate(pitch, 1.0f, 0.0f, 0.0f);
GlStateManager.color(1.0f, 1.0f, 1.0f);
model.bipedHead.render(1.0f);
model.bipedBody.render(1.0f);
model.bipedRightArm.render(1.0f);
model.bipedLeftArm.render(1.0f);
model.bipedRightLeg.render(1.0f);
model.bipedLeftLeg.render(1.0f);
model.bipedHeadwear.render(1.0f);
GlStateManager.depthFunc(GL11.GL_LEQUAL);
GlStateManager.popMatrix();
GlStateManager.disableColorMaterial();
使用方法,比如在主菜单渲染玩家:
private ScreenPlayerR
@EventHandler
public void init(FMLPostInitializationEvent event)
spr = new ScreenPlayerRenderer();
//别忘了在MinecraftForge.EVENT_BUS中注册事件句柄
@SubscribeEvent
public void onGuiRender(DrawScreenEvent.Post event) {
if(event.gui instanceof GuiMainMenu)
spr.setSkin(&szszss&);
spr.setWalk(1.0f);
spr.setHeadLook(-10f, 30f);
spr.render(55, 55, 10f, -30f, 0.0f, 4.0f, event.renderPartialTicks);
注册Forge/FML事件总线时出错
在Forge事件总线(EVENT_BUS、TERRAIN_GEN_BUS和ORE_GEN_BUS)和FML事件总线中注册事件总线时如果遇到了IllegalArgumentException异常的话,很可能是类中包含了带有@SubscribeEvent注解,但却有非法参数的方法.事件监听方法要求必须有并且只能有一个类型为Event或其派生类的参数.
注册事件无效,监听方法不被调用
注意监听方法的访问级,必须是非静态的公共方法,否则会被Forge默默地跳过...
此外还有其他因素,如果你的Mod在与其他Mod协同工作的话,事件是否被更高优先级的监听器拦截并取消?声明@SubscribeEvent注解时将receiveCanceled改为true可以接受被取消的事件;是否注册了恰当的事件总线?
总共有多少个事件总线
FML/Forge中总共有5条事件总线.Forge有3个通过FML的EventBus实现的,分别是处理所有的实体事件和绝大部分游戏事件的EVENT_BUS,处理地形生成的TERRAIN_GEN_BUS和处理矿物生成的ORE_GEN_BUS. FML有2个,分别是用自己的EventBus实现的(通过FMLCommonHandler.INSTANCE.bus()来获取)和用Guava的EventBus实现的(Coremod所使用的ModContainer的registerBus方法接受的那个参数即为该总线).
为什么有两套事件总线(FML的和Guava的)
大概是因为Guava事件总线处理的事件都是游戏初始化时的事件,此时FML尚未初始化完毕,总线系统尚不可用,因此就暂用Guava的.
Guava和FML事件总线的最大区别在于Guava事件总线是用反射来调用监听方法,FML事件总线是通过实时字节码生成来即时生成一个调用器,以直接调用的方式来调用监听方法,效率比反射高到不知道哪去了(顺便悄悄安利一下,我做的也是通过字节码生成的高效事件总线,当时以为是epic work,后来发现FML早在我之前就实现了...fucking life).
此外,FML的事件总线使用@SubscribeEvent来标记订阅者,普通Mod的主类用@EventHandler来标记初始化事件订阅者,而Coremod的事件处理类用@Subscribe来处理初始化事件订阅者.
为什么Forge和FML有独立的事件总线
客观原因:FML是一个取代ModLoader,负责实现Mod的加载和管理,以及必要的接口的工具,而Forge从最初到现在一直只是个功能库,虽然Forge出现早于FML,但在层次上现在的Forge是基于FML的,这注定它无法处理一些底层事物,比如玩家的输入以及服务器状态改变等.
主观原因:Forge和FML是两个联姻的不同项目,别忘了直到1.8的时候cpw宣布"Fuck, I quit"之前,FML一直是在一个独立的包下.
真要严格区分两者几乎是不可能的,如果只是粗略总结的话,Forge的事件总线用来处理顶层逻辑(比如实体之类的)以及GUI.FML的事件总线用来处理底层操作,比如玩家的鼠标键盘输入,服务器中玩家的登入登出.但由于各种历史原因,两者之间互有交集,比如Forge也会处理鼠标事件,而FML会处理冶炼和合成事件.
FML的事件总线能处理哪些事件
见cpw.mon.gameevent和cpw.mods.fml.client.event包下的类.
好吧我知道如果我不写出来的话有些人是永远不会去看的...这里大概写一下能处理什么功能.
玩家的鼠标与键盘输入操作.(FML将它归位了Common,不过这难道不应该是ClientOnly吗?算了不管它了...)
玩家的捡起物品,合成物品,取出冶炼物品,登录服务器,登出服务器,复活,(字面含义上)前往另一个世界
每帧(Tick)触发的事件,可以是ServerTickEvent(服务器每帧开始和结束时触发),ClientTickEvent(客户端每帧开始和结束时触发),WorldTickEvent(WorldServer的tick开始与结束时触发),PlayerTickEvent(EntityPlayer的onUpdate开始与结束触发)和RenderTickEvent(EntityRenderer的updateCameraAndRender开始与结束时触发)
客户端独有,ConfigChangedEvent改变Mod配置时触发
FML的Guava事件总线能处理哪些事件
见cpw.mon.event包下的类.基本上所有在Mod主类中能处理的那些事件都是由Guava事件总线来实现的.
如何查询全混淆名和半混淆名
问这个问题的人按说应该读过Coremod教程,那他是怎么才会没看到教程中的AsmShooterMappingData呢...不管怎么说,这里再贴一遍.
ASMShooter全称Aya Syameimaru's Miniskirt Shooter,简称ASMShooter,是本文作者开发的一个黑项目,现已由于过于黑,而被SCP基金会勒令停止开发并无限期冻结.说白了,这项目至今没出成果,而且已经停止开发了......
不过别急着关窗口,它的一个子项目已经开发完成了,我们可以利用它的产品:ASMShooterMappingData,进行查表定位.
ASMShooterMappingData是一个xml文件,它构建了一个完整的未混淆-半混淆-全混淆名的映射关系表,并且还详细记述了每一个方法的参数和返回值,非常便于查询.
更赞的是,在v2版协议中,它还增加了方法的描述符(由于FML内置的反混淆器存在,方法描述符在开发环境和游戏环境下都是通用的)和类的全名,更方便开发者快速查询信息.
ASMShooterMappingData在文件头的MinecraftVersion标识了它对应的Minecraft版本,ProtocolVersion标识了格式版本.当前的格式(Protocol 3)为:
--包结点(以Package命名 属性Name为包名)
--|--类节点(以Class命名 属性Unobscured为未混淆名,属性Obscured为混淆名,属性FullName为完整的未混淆类名)
--|--|--字段节点(以Field命名 属性Unobscured为未混淆名,属性Searge为半混淆名,属性Obscured为混淆名)
--|--|--方法节点(以Method命名 属性Unobscured为未混淆名,属性Searge为半混淆名,属性Obscured为混淆名)
--|--|--|--参数结点(以Param命名 内容文字为参数类型的未混淆名)
--|--|--|--返回值结点(以Return命名 内容文字为返回值类型的未混淆名)
--|--|--|--描述符结点(以Desc命名 内容文字为方法的未混淆描述符)
1.6.2,1.6.4,1.7.2,1.7.10:
本条目发布于。属于、、、分类。作者是。}

我要回帖

更多关于 我的世界mod闪退 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信