所有版权归丢人素学姐(正版id:YijunYuan)所有。
//赶出来的教程,过几天慢慢完善
这个东西其实在CBL外群和几个我摸鱼的群里都已经知道了,不过由于被@pca006132玩命催稿,那我就在论坛写一下好了。
注意:
- 这里的现实世界时间指的是运行该系统的计算机的机器时间
- 我不会给出用于生成mcfunction文件的代码。另外我不会为安装这个系统而造成地图的任何损坏负任何责任
- 根据1.13的快照的情况来看,这个系统无法直接运行在大于1.12.2的所有(快照)版本里。当然,主要的原理还是可行的,只是需要一些更改
- 可能在一段时间以后Panda4994会把这个系统的更新的版本放到Youtube上,当然这还没有定下来,会不会出现,怎么出现都还不一定
- 鉴于这并不是基于我的这个原理下最好的版本,而Panda4994和我都认为还能做更多改善(这我在后文中还会提到),他/她的存档我暂时不会发出来。当然不排除日后会发
- 安装该系统请使用只有y=0的一层的超平坦世界,否则可能会被埋在别的方块中(当然不影响功能)
- 安装只要将functions中的两个文件夹放进存档的functions并执行ooc.txt中的ooc即可
- 请在-5,1,-5附近安装,并把worldspawn设置到0,0,0附近,要看效果请进到生成的小黑屋,或者手持一个minecraft:clock看看效果
- 命令中可能有一些小问题,我慢慢改
记号与约定:
为了下文叙述简洁,我们不妨使用如下约定的记号:
- 讲某个现实时间中的时间点,也就是[小时:分钟:秒],记为(hh,mm,ss)的格式,如(23,48,59)即为23点48分59秒
- 下文的tick都不是红石刻。一般20tick=1秒
- 下文中的秒,指的都是现实时间中的秒
原理:
mc中,一般认为无法获取系统时间是因为,游戏中的时间是以tick为单位的。虽然我们都知道1s=20tick,但看过mc的代码就知道,这是建立在游戏不卡顿的前提下的。如果某一tick内要处理的“任务”非常多,那么这tick就完全有可能大于0.05秒(严重的卡顿甚至可以让1tick超过1秒)。
而另一方面,panda4994他们使用worldborder的方法,获得了每秒一次稳定且准确的脉冲。而这仍然是不够的,因为我们还是无法与现实时间“对齐”。也就是说,我们只能知道某段时间有多长(以秒为单位),而无法知道某个时间点的确切时间。即使我们手工把时钟和现实时间对齐了(比如在现实(00,00,00)把钟设置成(00,00,00)然后启动),在重启游戏,或者是单机模式下按了esc到菜单(此时游戏内tick是停止的)的情况下,时钟与现实时间就会失去同步。
因此,我们需要做到:
- 与现实时间的即时同步
- 在游戏中断重启后,在短时间内自动使时钟恢复与现实时间的对齐
实现第一点是基于对command block的输出的观察。
当我们使用cb执行一个命令时,我们可以观察到输出大致是这样的:
- [21:07:13] 数据标签未更改:{auto:1b,powered:0b,LastExecution:2357159L,SuccessCount:0,UpdateLastExecution:1b,conditionMet:1b,CustomName:"@",Command:"/setblock ~ ~1 ~ air 0 replace",x:-3,y:1,z:-19,id:"minecraft:command_block",LastOutput:"{"extra":[{"color":"red","translate":"commands.setblock.noChange"}],"text":"[20:42:10] "}",TrackOutput:1b}
如,我们不妨设在[x=0,y=0,z=0]放着一个repeating_command_block[auto=1b,face=north]{Command:"/ayaka"}。显然这个cb会一直运行失败,因为根本没有这样的命令,但我们获得了稳定的,带时间戳的输出。
我们可以使用另一个rcb X,来匹配[x=0,y=0,z=0]处的这个rcb,并具体到LastOutput:
- /testforblock 0 0 0 repeating_command_block 1 {LastOutput:"{"extra":[{"color":"red","translate":"commands.generic.notFound"}],"text":"[02:00:14] "}"}
但是由于Minecraft没有通配符,上面的这种操作一次性只能匹配一个时间点,而一天有24*60*60=86400秒,因此需要穷举所有这些时间点。
显然,使这些rcb一直全部处于工作状态是十分愚蠢的,这会使得游戏变得**卡顿(不用质疑这个结论,也不用再去试,因为我试过了。。。)。所以我们需要将这86400个工作单元分组。我将每15秒分为一组,每次执行一组中的cb,并在每组的最后一秒执行成功后,关闭这组并启动下一组。这样,这部分中,每tick最多就只有20个不到的命令在执行,基本解决了卡顿问题。另外,为了使组间切换更加方便,我对每一组用了一个必能执行成功的rcb Z来驱动每一组的cb,而每个工作单元中的X设置为conditonal=false的ccb,Y设置为conditonal=true的ccb,依次接在Z上,并在这串cb的最后接上两个conditonal的ccb,用于组间切换。
这样,这部分就一共有1+24*60*60/15*(1+15*2+2)=190081个cb。
我们将这部分称为常规系统。
现在我们解决第二个问题,也就是如何在这个系统无法同步(也就是当前显示时间并不在正在工作的这组cb的检测范围中)时,修复这个系统。
为了检测这个系统是否还在正常工作,每100tick检测一次,当前J中分数是否与40tick之前一样(这句话有点绕,但应该不难理解,实现起来也不难)。显然,如果这个系统正常工作着,那么J中的分数在经过40tick以后(注意40tick至少是两秒)必然会改变。而如果这个系统已经与现实时间脱离同步,即当前工作的那组cb中没有一个能匹配到当前时间,那么J中的分数就不会改变。
当检测到系统工作不正常时,我们启动修复系统。
修复系统会同时检测所有以下时间点:(*,*,0),(*,*,15),(*,*,30),(*,*,45),也就是对应着常规系统工作的那部分的cb的每组的第一个时间点。当然,修复系统工作的期间,存档会变得相对卡顿。当修复系统匹配到某个时间点时(h,m,s)时,会先将常规系统全部停止,再启动常规系统中包含(h,m,s)的那组。
这部分有24*4+24*60*4*30+10=17386个cb。
当然剩下的事情就简单了,用简单的计算(10个cb)我们把J中分数换算成时分秒分别放到计分板中,再用4*8=32个cb输出到一个告示牌(因为有些数字需要补0来对齐冒号,所以需要8组,每组来判断时,分,秒是否分别小于10,来决定计分板上的文字)。
另外,我检测了玩家是否手持一个minecraft:clock,如果拿着就把时间输出到title(subtitle提示MachineTime)。这部分需要44个cb(同样会有补0对齐的问题,所以需要的比较多)。
统计一下,这个系统一共用了190081+17386+10+32+44=207553个cb。
要放这么多cb,手工肯定是不可能的,因此我写了个mcfunction生成器,生成了二十几个mcfunction文件来批量放置cb。
改进:
- 修复系统每启动一次,最多需要花15+5=20秒让常规系统恢复正常工作,且在修复期间会比较卡顿。panda4994基于这个idea做了一个几乎瞬间修复且基本无卡顿的版本,但具体原理我还没有研究清楚,再加上我们都觉得1.13以后这个系统会修改,所以现在暂时不放出panda的版本。另外pca006132也提了一个方法,但我暂时懒得做了,他那个应该也很不错。
- 这个系统会使游戏的log爆炸。基本上4分钟以内MultiMC就会报出log已经超过100000行,所以请自行调整gamerule。
截图:
感谢:
- 玄素,在我刚形成这个idea时告诉了我很多技术性的细节
- panda4994,给了我很多技术上的建议,并制作了一个无卡顿的版本
- pca006132,玩命催我写教程
- CBL外群和RacerftCraft中的各位
- 等