Minecraft(我的世界)中文论坛

标题: 【1.12function】在原版Minecraft内获取现实世界时间(丢人钟)

作者: 丢人素学姐    时间: 2017-11-25 22:57
标题: 【1.12function】在原版Minecraft内获取现实世界时间(丢人钟)
本帖最后由 Vinogradov 于 2017-11-26 09:06 编辑

所有版权归丢人素学姐(正版id:YijunYuan)所有。


//赶出来的教程,过几天慢慢完善



这个东西其实在CBL外群和几个我摸鱼的群里都已经知道了,不过由于被@pca006132玩命催稿,那我就在论坛写一下好了。

注意:


记号与约定:
为了下文叙述简洁,我们不妨使用如下约定的记号:

原理:
mc中,一般认为无法获取系统时间是因为,游戏中的时间是以tick为单位的。虽然我们都知道1s=20tick,但看过mc的代码就知道,这是建立在游戏不卡顿的前提下的。如果某一tick内要处理的“任务”非常多,那么这tick就完全有可能大于0.05秒(严重的卡顿甚至可以让1tick超过1秒)。
而另一方面,panda4994他们使用worldborder的方法,获得了每秒一次稳定且准确的脉冲。而这仍然是不够的,因为我们还是无法与现实时间“对齐”。也就是说,我们只能知道某段时间有多长(以秒为单位),而无法知道某个时间点的确切时间。即使我们手工把时钟和现实时间对齐了(比如在现实(00,00,00)把钟设置成(00,00,00)然后启动),在重启游戏,或者是单机模式下按了esc到菜单(此时游戏内tick是停止的)的情况下,时钟与现实时间就会失去同步。
因此,我们需要做到:

实现第一点是基于对command block的输出的观察。

当我们使用cb执行一个命令时,我们可以观察到输出大致是这样的:
注意红圈里的文字,这不就是我们所需要的时间吗!对着这个cb来一发blockdata,可以得到这些:
  1. [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}
复制代码
如果我们能保证输出稳定不变,那就可以通过testforblock来匹配某个固定的时间点了。
如,我们不妨设在[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:
  1. /testforblock 0 0 0 repeating_command_block 1 {LastOutput:"{"extra":[{"color":"red","translate":"commands.generic.notFound"}],"text":"[02:00:14] "}"}
复制代码
这样,在(02,00,14)时,X就会执行成功。我们在X上接一个conditional的ccb Y,使得X执行成功时,Y可以将当前时间距离(00,00,00)的秒数,即hour*24*60+minute*60+second写入计分板。(可以使用三个ccb,或者写一个function将时,分,秒直接分别写入记分板J,但是这样会使得ccb或者mcfunction文件大量增加,影响游戏体验,所以我没有这样做)
但是由于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。

改进:

截图:


感谢:
Time.rar (477.96 KB, 下载次数: 119)


作者: 3261298764    时间: 2017-11-25 23:10

大佬就是不一样
作者: Mithey    时间: 2017-11-26 06:58
本帖最后由 Mithey 于 2017-11-26 06:59 编辑

我不会给出用于生成mcfunction文件的代码。另外我不会为安装这个系统而造成地图的任何损坏负任何责任

摔锅
作者: :spgbigfan:    时间: 2017-11-26 09:16
穷举为所欲为
作者: BILO    时间: 2017-11-26 17:14
好复杂啊。。。(支持一下)
作者: sjx    时间: 2017-12-3 19:58
用cb/function完成复杂任务,总要用穷举
不穷举怎么行?
作者: artsin    时间: 2017-12-19 18:45
厉害了。。。
作者: fengzhihui    时间: 2019-9-23 15:18
6666666666
隧道炉
作者: boomfire    时间: 2020-2-2 06:57
提示: 作者被禁止或删除 内容自动屏蔽
作者: 慵懒的橙子    时间: 2020-2-2 10:11
谢谢 我顺便来做个任务
作者: 霖邈a    时间: 2020-2-2 11:44
感谢你的帮助666
作者: 我叫伯爵    时间: 2020-2-18 18:53
围观楼上几个警告
话说这么多cb放在function游戏真的不会卡顿么
作者: 丢人素学姐    时间: 2020-2-20 11:15
我叫伯爵 发表于 2020-2-18 18:53
围观楼上几个警告
话说这么多cb放在function游戏真的不会卡顿么

现在V3只剩1个cb了,别的都放function里,而且每tick只有几十个命令在跑,不会卡。

另外需要说明的是现在能拆字符串了,那丢人钟基于穷举的实现就显得没什么意义了。
作者: 《无语》    时间: 2020-5-2 21:14
这令我的服务器崩了
作者: nybornhawk    时间: 2021-1-23 17:48
新技能get + 1