第九十九章 初识模块-简单的注册/登录系统 下
(此章节已于2022年7月31日重写)
在上一章,我们解决了一个世纪难题:输入。这一章,我们将会尝试实现『注册』这个功能。
我们先理一下这个系统到底是怎样的:
①有一个br /assword触发器,存储着玩家的密码,注册时玩家要填写的字段
②有一个is_tybr /ed计分项,用来检测玩家是否填写了密码
③玩家按下按钮后,为玩家打开br /assword触发器,并指示玩家通过/trigger指令填写密码。玩家填写密码后,检测玩家的密码是否符合要求,如果符合则通过检测并传送走玩家,如果不符合则返回错误信息,并再次为玩家打开触发器指示玩家填写。
理清楚之后,我们就可以开始动工了。我们将会采用前几章刚了解的『红石逻辑组』作为基础来建设这个系统。先搭建一个简单的结构:
◎墙→A→B (插图99-1)
◎······按钮
→······红石中继器
首先,既然要『玩家按下按钮后,为玩家打开br /assword触发器』,那么第一个命令方块A就应该填写:
scoreboard br /layers enable @br / br /assword
\\为最近的玩家开启它的br /assword计分项\\
命令方块B用来实现『指示玩家通过/trigger指令填写密码』,因此填写:
tellraw @br / {“text“:“请运行指令\“/trigger br /assword set <密码>\“输入账户密码来注册“,“color“:“yellow“}
\\告诉最近的玩家『请运行指令“/trigger br /assword set <密码>“输入账户密码来注册』\\
然后呢?我们需要一个一直处于运行状态的红石逻辑组来不断检测玩家是否已经输入密码,所以我们要在旁边搭建一个红石脉冲并配上一些命令方块:
脉冲→C→D→E (插图99-2)
C:execute as @br / store success score @s is_tybr /ed run scoreboard br /layers enable @s br /assword
\\检测玩家是否输入密码\\
D:tellraw @br /[scores={is_tybr /ed=1}]{“text“:“注册成功,请记好你的密码哦!“,“color“:“green“}
\\给已输入密码的玩家发送成功提示\\
E:execute as @br /[scores={is_tybr /ed=1}] run tbr / @s 13 -60 10 180 0
\\把注册成功的玩家传送走\\
这样子,我们的Beta1.0注册登录系统就做好了!让我们来试一试:
*按下按钮*
请运行指令“/trigger br /assword set <密码>“输入账户密码来注册
*输入指令:/trigger br /assword set 114514*
已触发[br /assword](数值已设置为114514)
注册成功,请记好你的密码哦!
*被传送走*
运作得十分棒!但是,如果你再次尝试使用trigger指令的话......
*输入指令:/trigger br /assword set 1919810*
已触发[br /assword](数值已设置为1919810)
注册成功,请记好你的密码哦!
?我不是注册过了吗?
没错,这个Beta1.0的系统漏洞百出,它起码有这几个问题:
①已注册的玩家可再注册
②br /assword一直可以被修改
③可能无法很好应对多玩家情况
④系统运作较慢
⑤对于后续扩展出『登录』功能不友好
为了解决这些问题,我们可以采用tag标签对玩家进行标记。那么我们会用到哪些tag标签呢?
•对于注册中的玩家,需要registering标签
•对于已经注册的玩家,需要registered标签
•对于处于登录中的玩家,需要logging_in标签
•对于已经登录的玩家,需要logged_in标签
其中,register是注册的意思,log in是登录的意思。
我们需要在玩家刚刚开始注册/登录时给玩家添加上registering或logging_in标签以防止选择到那些已经登录和注册的人,给那些注册成功的玩家添加上registered标签和logged_in标签并移除掉registering标签,给那些登录成功的玩家添加上logged_in标签并移除掉logging_in。在后面,我们还会使用更多的标签来完善整个系统的运作。
另外,我们还需要关闭成功注册玩家的br /assword触发器以防止注册了还能再修改br /assword,还需要在每次红石逻辑组结束运行时重置每名玩家的is_tybr /ed的分数值。前者我们可以通过execute以玩家为执行者运行一遍trigger br /assword add 0来实现,后者可以通过加上写有『scoreboard br /layers set @a is_tybr /ed 0』的命令方块来实现。
让我们在系统中加入标签进行筛选,并加入上面的指令,看一看是否能够解决一些问题:
◎墙→A→B→C(插图:99-3)
脉冲→D→E→F→G→H→I→J
A:execute br /ositioned ~~~-4 run tag @a[distance=..3,tag=!registered,tag=!logged_in] add registering
\\给未登录且未注册的玩家添加registering标签\\
B:scoreboard br /layers enable @a[tag=registering] br /assword
C:tellraw @a[tag=registering]{“text“:“请运行指令\“/trigger br /assword set <密码>\“输入账户密码来注册“,“color“:“yellow“}
D:execute as @a[tag=registering] store success score @s is_tybr /ed run scoreboard br /layers enable @s br /assword
E:execute as @a[tag=registering,scores={is_tybr /ed=1}] run tag @s add registered
\\给成功注册的玩家添加registered标签\\
F:tellraw @a[tag=registering,scores={is_tybr /ed=1}]{“text“:“注册成功,请记好你的密码哦!“,“color“:“green“}
G:execute as @a[tag=registering,scores={is_tybr /ed=1}] run trigger br /assword add 0
\\将成功注册玩家的br /assword触发器使用一遍以关闭\\
H:execute as @a[tag=registering,scores={is_tybr /ed=1}] run tbr / @s 13 -60 10 180 0
I:execute as @a[tag=registering,scores={is_tybr /ed=1}] run tag @s remove registering
J:scoreboard br /layers set @a is_tybr /ed 0
\\重置is_tybr /ed\\
现在,我们的『注册/登录系统』Beta2.0测试版出来了!让我们测试一下:
n个『已将XXX的[is_tybr /ed]分数设为0』
*按下按钮*
已为XXX添加了标签'registering'
请运行指令“/trigger br /assword set <密码>“输入账户密码来注册
n个『已将XXX的[is_tybr /ed]分数设为0』
*输入密码*
已触发[br /assword](数值已设置为1919810)
已为XXX启用了触发器[br /assword]
已移除XXX的标签'registering'
n个『已将XXX的[is_tybr /ed]分数设为0』
......
出BUG了,还是个很严重的BUG。
不要慌张,让我们来排查一下到底是什么问题。
仔细阅读上面的日志,你会发现:
由于红石逻辑组的特性,在过长的逻辑组中,执行顺序会难以把控,然后就出现了上面的问题。
那该怎么办?
改用『纯指令逻辑组』。
『纯指令逻辑组』,其实是目前最常见的逻辑组。它有一个更加专业的说法:模块。在本书中,它也被称之为『命令块链』。
我们其实已经见过很多次『纯指令逻辑组』了,它其实就是由一个脉冲或循环型命令方块+一大串连锁型命令方块组成的。比如下面的这串命令方块:
A→B→C→D→(插图:99-4)
(为了方便,下面我都会称这东西为『模块』)
在这边,箭头代表着命令方块的朝向,A为循环型,BCD均为连锁。这些命令方块的朝向使得它们环环相扣。
循环型命令方块A是整个模块的核心,它负责整个模块的更新。如果A还是『始终活动』,那么A还具有维持整个模块运行的功能。我们从A是循环型这一点也不难看出,这个逻辑组还是个重复型逻辑组。
连锁型命令方块BCD是整个模块的通路。相比红石逻辑组通过红石元件来控制执行顺序,模块通路的执行顺序更加直观也不容易出乱子——因为它是通过命令方块的朝向来决定执行顺序。这也就是为什么在上面的模块中,执行顺序是从左到右的原因。
模块的难点在于对通路的理解,更确切地说是对『连锁型命令方块』运作机制的理解。还记得我们在第二章提到的内容吗?让我们回顾一下:
『青得一批的那个是链(又叫做连锁),作用是你叫它动一下(接入红石信号),它不会动。它只会在收到执行信号时,将该执行信号立马传递给下一个它指向的链命令方块,然后再根据当前条件判断是否要执行。听起来似乎有点复杂?举个例子:
E→F→G→H→
其中,E是未被激活的脉冲命令块,FGH三个命令方块都是已被激活且无条件限制的链命令块。当E激活时,EFGH将会同时执行指令,因为E激活时向F发出了执行信号,F收到后也向G发出了信号,G也向H发出了信号,三个链命令方块也都没有条件限制。』
现在,让我们仔细研究一下上面的EFGH为何会这样子执行。为了搞清楚为何这样子,我们就得研究一个东西:
执行信号
可以肯定的是,游戏本身并没有『执行信号』这东西,但为了方便大家理解,我就根据命令方块的性质总结出来了『执行信号』这个东西。那么这东西有什么特别的呢?
脉冲型和循环型命令方块,在被激活的同时不仅仅会尝试运行指令,还会产生执行信号并尝试将其传递给它所朝向的连锁型命令方块。
连锁型命令方块不管是无条件还是条件限制、需要红石还是始终活动、有被激活还是没被激活,只要它收到了执行信号,那么它就一定会尝试将该信号传递给它所朝向的下一个连锁型命令方块,当然它在传递的同时也会根据当前条件决定是否要运行指令。
举个简单的例子:
甲→乙→丙→丁→(插图:99-5)
甲:脉冲型,需要红石,无条件,没有写指令
乙:连锁型,没有写指令
丙:连锁型,没有写指令
丁:连锁型,需要红石,无条件,写有/setblock ~~1 ~ diamond_block
当我们同时激活甲和丁时,尽管甲乙丙都会因为没有填写指令而执行失败,但丁却因为收到了由甲产生并经过乙丙传递的执行信号,加上本身已被激活且没有条件限制,就执行了指令,在上面放置了一个钻石块。
这就是执行信号。但连锁型命令方块并不一定会在接收到执行信号后立马就执行指令,它还会看一下情况:
①如果自身未被激活
→不会运行指令,仅仅会传递执行信号
②如果自身已被激活
→1.如果没有条件限制
→→尝试运行指令,然后再传递执行信号
→→否则不会运行指令,仅仅会传递执行信号
→2.如果有条件限制
→→如果正后方的命令方块上一次执行指令成功
→→→尝试运行指令,然后再传递执行信号
→→→否则不会运行指令,仅仅会传递执行信号
举个例子:
戊→己→庚→(插图:99-6)
戊:脉冲、不受制约、需要红石,填有『setblock ~~1 ~ minecraft:diamond_block』
己:连锁、条件制约、始终活动,填有『say yes』
庚:连锁、不受制约、始终活动,填有『say no』
当我们第一次激活『戊』命令方块时,戊就会在上方放一个钻石块,己由于戊执行成功,就会发送『yes』的消息,庚由于不受制约所以肯定会发出『no』的消息,最终三个命令方块都运行了指令。
当我们第二次激活『戊』命令方块时,由于戊上方已经有钻石块了,所以指令执行失败。己由于戊执行失败,自己也不会执行。最终只有戊和庚执行并且只有庚执行成功。
如果你能看懂并理解上面的内容,那么恭喜你,你已经基本上掌握了连锁型命令方块的运作机制,模块本身最难以理解的点已被攻破。现在让我们回到注册/登录系统上,尝试以模块的形式替代原有的红石逻辑组:
◎墙A→B→C→(插图:99-7)
D→E→F→G→H→I→J→
A[脉][无][红]——execute br /ositioned ~~~-3 run tag @a[distance=..3,tag=!registered,tag=!logged_in] add registering
B[链][无][始]——同替代前
C[链][无][始]——同替代前
D[重][无][红]
E[链][无][始]
F[链][无][始]
G[链][无][始]
H[链][无][始]
I[链][无][始]
J[链][无][始]
现在,我们系统的Beta3.0版本出来了,让我们测试一下BUG有无解决:
*按下按钮*
已为XXX添加了标签'registering'
请运行指令“/trigger br /assword set <密码>“输入账户密码来注册
n个『已将XXX的[is_tybr /ed]分数设为0』
*输入密码*
已触发[br /assword](数值已设置为1919810)
已为XXX启用了触发器[br /assword]
已为XXX添加了标签'registered'
注册成功,请记好你的密码哦!
已触发[br /assword](数值已增加0)
已将XXX传送到 13.500000,-60.000000, 10.500000
已移除XXX的标签'registering'
n个『已将XXX的[is_tybr /ed]分数设为0』
解决了!!!
现在,我们已经完成了这个系统的大半部分,接下来我们将会尝试完成『检测玩家的密码是否符合要求,如果符合则通过检测并传送走玩家,如果不符合则返回错误信息,并再次为玩家打开触发器指示玩家填写』这个部分。
其实这个部分相当简单。比如我们想要玩家不能设定密码为0,就可以这么干:
在D命令方块后面插入两个新的命令方块,写入以下内容
execute as @a[tag=registering,scores={is_tybr /ed=1}] unless score @s br /assword matches 0 run tag @s add correct_inbr /ut
\\为密码不是0的玩家添加标签correct_inbr /ut(正确输入)\\
execute as @a[tag=registering,scores={is_tybr /ed=1}] if score @s br /assword matches 0 run tag @s add error_inbr /ut
\\为密码是0的玩家添加标签error_inbr /ut(错误输入)\\
修改E、F、G、H、I命令方块的内容为
execute as @a[tag=correct_inbr /ut,tag=registering] run tag @s add registered
tellraw @a[tag=correct_inbr /ut,tag=registering]{“text“:“注册成功,请记好你的密码哦!“,“color“:“green“}
execute as @a[tag=correct_inbr /ut,tag=registering] run trigger br /assword add 0
execute as @a[tag=correct_inbr /ut,tag=registering] run tbr / @s 13 -60 10 180 0
execute as @a[tag=correct_inbr /ut,tag=registering] run tag @s remove registering
在I后新增命令方块3个,内容为
execute as @a[tag=correct_inbr /ut,tag=registered] run tag @s remove correct_inbr /ut
execute as @a[tag=registering,tag=error_inbr /ut] run tellraw @s {“text“:“请不要将0作为你的密码,请重新输入“,“color“:“red“}
execute as @a[tag=registering,tag=error_inbr /ut] run tag @s remove error_inbr /ut
让我们测试一下:
......
*输入密码*
已触发[br /assword](数值已设置为0)
已为XXX启用了触发器[br /assword]
已为XXX添加了标签'error_inbr /ut'
请不要将0作为你的密码,请重新输入
已移除XXX的标签'error_inbr /ut'
*再次输入密码*
已触发[br /assword](数值已设置为1)
已为XXX启用了触发器[br /assword]
已为XXX添加了标签'correct_inbr /ut'
已为XXX添加了标签'registered'
注册成功,请记好你的密码哦!
已触发[br /assword](数值已增加0)
已将XXX传送到 13.500000,-60.000000, 10.500000
已移除XXX的标签'registering'
已移除XXX的标签'correct_inbr /ut'
OK,完美运行,这就是系统的Beta4.0版本。
只不过由于篇幅限制,我们就讲到这里。接下来对于『登录』功能甚至『登出』功能的实现,请你自己独立完成。这边有几个要求和提示:
①登录模块和注册模块要分开,但其实两者的原理都差不多
②创建一个新的触发器enter_br /assword,用于登录密码的输入
③优化模块的运行,使得玩家在按下按钮后才会调用登录和注册模块,并在没有处于登录/注册中的玩家后停止模块运行(提示:setblock或fill红石块)
④创建一个新的触发器,已登录的玩家可以修改此触发器到一个特定的值来退出登录
⑤新玩家进来后必须要出生在注册/登录房里
⑥未追踪玩家的分数无法被比较。所以如果你拿未追踪玩家的分数来比较,由于没有分数,所以游戏总会返回false(假)
你可以加QQ群或通过作家的话中的网盘链接获取到我做的完整『注册/登录系统』的存档(Minecraft Java1.19版本,非网易版)。
本章到此为止。