关于怪物猎人世界的个人弃坑感受

最近一段时间,在备考的空闲里,我都会玩怪物猎人世界。虽然使用大锤稳扎稳打到了上位HR13,但是在今天突然觉得是时候放弃了。放弃不是说这个游戏太难、我打不通,而是它很无趣。这种无趣,或许就是Roguelike游戏所解决的问题。

虽然很多游戏玩家对于怪物猎人的评价很高,大多数都对其动作系统赞不绝口,但是动作系统的出色并不能掩盖它在其他方面的缺点——又或者,在某些玩家的眼里,这正是怪物猎人的核心玩法。

最大的缺点莫过于,在初期,你可以很轻松地切换不同的武器,毕竟前期的怪物伤害不高,而且不同武器的核心技能在前期是不可得的——装备穿哪件都一样;而到了中期,甚至都还不需到后期追求流派的时候,就已经非常难以切换武器了:好不容易配出来一套防御7耳塞3的装备来玩莽大锤,但是如果要换大剑的话,又要憋很久才能出集中3。打造装备需要非常非常多的材料,在龙结晶之地挖矿挖到神智不清的时候,我就已经心生厌倦。而这么一个“装备驱动”游戏,你千辛万苦打出来的装备,在面对恐暴龙的时候完全就像纸,这种挫折难道就是婆罗门口中所说的“人类的勇气”吗?我只感觉到被游戏嘲笑。

动作游戏,无论是宫崎英高的黑魂、只狼或者老头环,还是师父,其实很讲究酣畅淋漓。要打就堂堂正正地打,有来有往,你出的招我有办法化解是很关键的。怪物猎人在这方面怎么做的呢?婆罗门说,你要学会观察,要对怪物了解,知道它要出什么招,然后才能打得爽快。算了,既然这里是我的博客,我就大胆说了,放你娘的屁。我真是信了你的鬼话才想着“抓怪物的后摇”,恐暴龙那个连着出招、攻击判定大到离谱的攻击方式,拿着锤子蓄力出击还没打出来就被操飞,你告诉我成就感在哪里?每个怪都他妈口臭一样动不动就吼,前摇又短,而且我是人类,我怎么知道这个狗*什么时候要吼、为什么吼?耳塞5是休闲玩家的标配,但是你妈的HR13装备大丰富的时候还是根本配不出来一套耳塞5,最多耳塞3。你在怪物头前面锤子3蓄打完两个体操正准备第3下爆炸输出的时候,打点真的很美的时候,它给你一口臭,你就立刻收招、手掌超越光速捂上耳朵跟个傻逼一样站在那里,你跟我说这叫真实?每次见到这个口臭招的时候,血压都是那叫一个高,突出一个打断节奏,这么粪的机制居然有人觉得“真实”,我都不知道玩这个干嘛。

其实说来很简单,老头环让我吐槽的一点就在于,boss性能堪比只狼的情况下褪色者还是跟ashen one一样小儿麻痹,这就导致了一种被霸凌的情况,这是很不爽的。到了怪物猎人这里,就纯粹是怪物上蹿下跳超高性能,而猎人又笨又重被终极侮辱。

所以今天搜索了一个很实在的问题,为什么长大了就不喜欢玩游戏了。很多人回答的是,现实世界带来的成就感和刺激感比游戏能带来的强太多了。其实更重要的是,我是来玩游戏的,不是被游戏玩的。为什么师父同样很难,但是打完一场就酣畅淋漓,而你怪物猎人就从来没有这种爽快过?我觉得不是人的问题,是你卡普空的问题。

Read More

adguardhome隐蔽提供DoH服务

adguardhome是个好东西,经常被用来装在家里的路由器上,给家里网络去广告。但是如果想把它装到VPS上提供“私有”的DNS服务,那就会麻烦很多——从安装开始就在折磨。还好,折腾了一番之后终于搞好了,所以写下这篇“指南”。

目录结构

首先来介绍目录结构:

  1. nginx目录:/etc/nginx,vhost配置文件目录:/etc/nginx/sites
  2. adguardhome,用docker-compose来搭,按照官方建议有以下结构:
1
2
3
4
/opt/adguardhome
|-- confdir/
|-- workdir/
`-- docker-compose.yaml

首先参考一下adguardhome的docker页面,主要是看它说明的不同的端口分别是用来做什么的,以决定需要映射什么端口出来:

端口 功能
53 普通DNS
67、68 DHCP功能
853 DoT服务
784、853、8853 DNS over QUIC服务
5443 DNSCrypt服务
80、443、3000 管理页面及DoH服务

可见,我们需要持续用到的是443端口,在安装时需要映射80及3000端口才能访问安装页面。

初步安装

首先从reddit抄一个docker-compose.yaml来(只是看看,别学,用不了的):

Read More

如何用bare git来让服务器端负责生成静态博客

note

由于已经停止使用gogs而改为使用gitlab,加之后来把gogs搬进docker的时候无法直接使用post-recieve了,所以改成了webhook激发uwsgi的生成方式。推荐阅读:webhook生成静态博客

类似Hexo、Hugo、Jekyll和Pelican等静态博客生成器,通常来说都是在本地生成之后再上传到服务器的。上传到服务器的方式通常是用rsync来同步output文件夹,而这样做有一个问题:即使主要内容没有更新,很多html还是会发生变化(例如新增一篇文章之后,所有的index页面都发生了变化),导致每次几乎都是全部重新上传,等待时间极长。以前是非常苦恼于这个问题,后来看jekyll的manual,从它的自动部署篇获取了知识,经过一番改造,成功搞出了现在的git push之后在服务器生成的操作流程。

这种生成方法的优势在于只需要上传发生了变化的源文件(外加git的数据),相比较之前每次都是全量上传,total size大大减少,对于小水管来说体验提升很多。劣势则是需要服务器端也安装与本地相同的生成环境,不仅第一次部署的时候需要花费时间,后续如果本地装了新的插件则需要再上服务器安装相同的东西,保证环境同步。不过对于一个稳定的博客来说——此处指不再折腾博客背后的技术,把心思放在写博客上——生成环境是不怎么变化的,所以劣势也不会太影响体验。

Edit:通过网上冲浪,学会了jekyll的生成时安装依赖的操作,这个部署环境的麻烦也减少了很多。

本文主要是根据 Jekyll Automated Deployment 改造而来的。

Bare Git

在进行这些操作之前,首先需要你的服务器上有名为git的用户,而且服务器的ssh能够让git用户登录。

目录结构与服务器环境

服务器的域名 git.remote.domain
用于自动生成和publish的服务器用户 git
nginx读取的用于提供静态网页的目录 /var/www/blog
bare git的目录 /opt/blog.git
本地blog的目录 /home/useranme/blog
gogs目录 /opt/docker/gogs

需要注意,nginx的owner和group通常是http,为了让git能够写入/var/www/blog同时其内容又可以被nginx读取,就需要把/var/www/blog的所有权设置成git:http

Read More

Contribute to Archcn Society的意义似乎是没有的

今天在翻译archwiki的CN站的PKGBUILD页面的时候,一个念头突然在心里萌生并且迅速占据了上风:我做的这些翻译工作,是没有用的,没有意义的。

为什么这么说呢,因为看的人不会用,会用的人不会看,需求错位。

逐步分析哈,从最广泛的角度来讲,会来搞archlinux的人,英语能力肯定是足以阅读英文版的wiki的;如果连阅读英文维基的能力都没有,那就不会安装archlinux了。

再具体来说,当一个人已经开始contribute package,那么他一定知道一个事实:英文版的内容是up to date的,按照英文版的内容来做肯定不会错。PKGBUILD这种页面的阅读者甚至不是简单的“装了就行”的普通用户,而是对于Linux已经有很多的了解(我认为,至少是会编程的人,毕竟所有package即使不需要compile,多少也是python这样的,最不济-bin类型的包也涉及shell)的用户,这样的用户怎么会需要看中文维基呢?

所以我决定不搞了。在AUR上维护包对于社区的贡献比维护中文维基的还大。

Read More

Bili录播姬webhook2触发本地python转换录播

bili录播姬输出的录播文件是flv格式,附带弹幕文件是xml格式。对于多数播放器而言,可接受的外挂字幕格式一般都是ass或srt。此外,如果想用iPhone来看录播,就需要把字幕和视频合在一起,因为视频播放器只会从在线字幕网站搜索字幕,无法加载ass字幕文件。综上所述,需要实现的目标是:1,将xml弹幕转为ass弹幕;2,如果出于某些原因,没有弹幕文件,那么就将flv转为mp4,否则把flv和ass合并为mkv。

之前写了一个简单的python来调用系统的ffmpeg和danmakufactory来转换,用systemd timer来定时触发。这种方法好是好,但是做了个assumption:主播不会在凌晨四点还在播;万一主播通宵了就不太好使了。当初设置成固定时间触发,是因为没办法知道录播什么时候结束。但是录播姬提供了一个功能(webhook),可以用来作为触发器。经过一些研究,可以得到以下的知识:

  1. 录播姬的event我们只需要关心文件关闭即可。即使因为网络波动,一个录播断成了几段,作为转换器的我们不care这件事,我们只关注文件关闭——文件已关闭,这个文件不会再有新数据写入,所以我们做转换得到的输出文件内容跟这段录播是一样的,不会出现以后需要重新访问、重新转换的情况。
  2. 在使用ffmpeg把ass字幕作为一条stream塞进mkv里面的时候,如果不把字体文件也attach进去,会产生报错——对于缺少这个字体的系统,在执行ffmpeg做转换的时候就已经会提示;否则,在播放的时候也会出问题。

转换函数

首先把执行转换用的函数准备好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import os
import sys

def extractfilename(fullpath):
return os.path.basename(fullpath)

def silentremove(filename):
try:
os.remove(filename)
except Exception as e:
print(str(e))

def transform(videofilepath):
# 录播姬工作目录
SRC_ROOT="/mnt/sharedisk/bilibili_rec/"
# 字体文件
FONT=SRC_ROOT+"font/Microsoft-YaHei.ttf"
# 输出目录
DST_VIDEO_DIR='/mnt/sharedisk/B站录播/视频/'
DST_AUDIO_DIR='/mnt/sharedisk/B站录播/音频/'
# 调用DanmakuFactory转换xml到ass的shell命令的模板
DANMUFORMATSTR='DanmakuFactory -o ass "{}" -i xml "{}" -r "1920x1080" -S "52"'
# 调用ffmpeg把flv+ass转成mkv的shell命令模板
FLVTOMKV = 'ffmpeg -i \'{}\' -i \'{}\' -attach \'{}\' -c:v copy -c:a copy -map 0:0 -map 0:1 -map 1:0 -metadata:s:t mimetype=application/x-truetype-font -y \'{}\''
# 调用ffmpeg把flv转成mp4的shell命令模板
FLVTOMP4 = 'ffmpeg -i \'{}\' -y -vcodec copy -acodec copy \'{}\''
# 调用ffmpeg把flv只提取音频轨道转成m4a的shell命令模板
FLVTOM4A = 'ffmpeg -i \'{}\' -map 0:a -c copy \'{}\''

# 从传入的videofilepath中去掉.flv后缀,生成后续的一系列源文件名
src_common_name = videofilepath.removesuffix(".flv")
# 从传入的videofilepath中去掉.flv后缀,生成后续的一系列输出文件名
dst_common_name = extractfilename(videofilepath).removesuffix(".flv")
# 这两条有所不同,因为源文件们可以直接使用录播姬传进来的带目录的文件名
# 录播姬传进来的是 录播间名/录播文件.flv,毕竟datafield的tag是relativepath
# 但是输出文件名们都是用另外的输出目录,所以要把这个前导目录给去掉,只留文件名部份

# 弹幕源文件
xml_file = os.path.join(SRC_ROOT, src_common_name+".xml")
# 弹幕输出文件
ass_file = os.path.join(SRC_ROOT, src_common_name+".ass")
# 录播源文件
flv_file = os.path.join(SRC_ROOT, videofilepath)
# mp4输出文件
mp4_file = os.path.join(DST_VIDEO_DIR, dst_common_name+".mp4")
# mkv输出文件
mkv_file = os.path.join(DST_VIDEO_DIR, dst_common_name+".mkv")
# m4a输出文件
m4a_file = os.path.join(DST_AUDIO_DIR,dst_common_name+".m4a")
# 防呆
if not os.path.exists(flv_file):
return "File not exists."
# 如果有弹幕文件,先把ass给删了,重新生成ass
if os.path.exists(xml_file):
silentremove(ass_file)
os.system(DANMUFORMATSTR.format(ass_file,xml_file))
# 同理,删掉所有曾经的输出文件
silentremove(mkv_file)
silentremove(mp4_file)
silentremove(m4a_file)
# 有ass就转成mkv
if os.path.exists(ass_file):
os.system(FLVTOMKV.format(flv_file,ass_file,FONT,mkv_file))
print("Successfully convert flv to mkv, subtitle inserted.")
# 没ass就转成mp4
else:
os.system(FLVTOMP4.format(flv_file,mp4_file))
print("Successfully convert flv to mp4, no subtitle found.")
# 生成音频
os.system(FLVTOM4A.format(flv_file,m4a_file))
# 收拾手尾
silentremove(ass_file)

FONT变量写的是之后内嵌到mkv文件里的字体文件。我们用的是在mkv容器中新增一个轨道放置ass字幕的方法来写入弹幕,对于播放器而言就需要在播放中渲染字幕的时候找到字体文件才能渲染,因此我们就需要把字体文件也嵌入mkv中。

录播姬默认设置的弹幕样式的字体是微软雅黑,所以搞到一个微软雅黑的字体文件,放在录播姬工作目录下的font文件夹里,再用FONT变量写明路径,用于之后ffmpeg-attach进容器里。

为什么用mkv?因为mp4嵌入字幕需要用video filter,慢到烂,最关键的是因为是re-encode,所以画面会降级(非常明显)。我宁愿另外装app来播mkv,也不要等几个钟来重新渲染。mkv虽然没办法用系统自带app播放,但是其实解码耗电是一样的,因为mkv和mp4都是容器格式,真正的视频格式还是h.264,iPhone和Mac都能硬解。

为什么代码那么烂?谁会把python写得好看,又不是C++

Read More

随想

今天和子莹家里吃饭,是去的海鲜市场那边。去之前回了一下爸妈家拿手信,但是结果送她回家的时候忘了给她——因为放在车尾箱里了。今天才知道她没有跟爸妈说我已经脱产备考的事情,所以只能在饭桌上说了,他们倒也没有特别奇怪。

今天才发现,背单词大量消耗脑力,导致困的时间提前了很多。另外,因为背单词大量占用了短期记忆,所以陷入了初三和高三同样的那种,生活里发生的事情迅速忘记,的状态。这也是写下这篇日记的原因,需要靠“外部储存”记录一下生活,不然未来会忘记。

昨天把家里的网络布局改回传统的模式了,就是光猫->无线路由器的模式,3100的台式机作为服务器直连无线路由器的LAN1,5600X的台式机直连无线路由器的LAN2,其他设备连Wi-Fi。主要原因是,3100如果做主路由,是没法稳定跑满300M宽带的,昨天在用百度云拉满带宽的时候发现了这个问题——表现为下载速度在7M/s(56M)和40M/s(320M)之间来回波动。我猜是因为软路由在高负载情况下,通用计算CPU还是顶不住这样的数据包冲击,不如硬件电路转发来得快——形象地说,CPU堵车。

最近接手了AUR上几个包的维护,主要都是bili录制相关的。

Read More

别再折腾Gentoo了!

晚上花了亿点时间折腾gentoo,然后在凌晨1点的时候发现无意义,并选择了放弃。gentoo当然好玩,但是我已经不是大学生了,没有无限的自由时间去搞这些并不能带来什么技能提升的东西。大学的时候还可能因为接触没了解过的Linux发行版而对Linux更加了解,但是现在已经完全理解了Linux就是工具平台,既不适合做Desktop,也不值得消耗我的时间在搭建平台上。对于正常人来说,一个合理的发行版应该是稳定、快速部署的,而不是从源码编译那么耗时的。

Read More

nginx自签名证书与client auth机制

在之前的 nextcloud-fpm + frp + nginx + ssl 一文中,公网服务器的Nginx与家中服务器的Nginx通信是是使用自签名证书的。这样做主要的原因是家中的服务器没有公网IP,无法直接获取公共CA签名的证书;而定时从服务器复制证书下来的操作非常不方便,不值得。但是自签名证书容易被中间人攻击是广为人知的,而为了解决这个问题,今天无意间看到client authentication。大概意思是,传统的HTTPS是客户端验证服务器证书是否有效,而client authentication顾名思义就是加多了服务器验证客户端证书是否有效这一方向,所以也称为双向验证。理论上来说这可以提高安全性,不管怎么说,折腾一下总没错的。

本文会介绍:

  • 自签名CA及自签名证书
  • 双向证书验证(Nginx)

1. 自签名CA及自签名证书

生成CA及自签名证书需要分开来做。

1.1 CA

生成自己的CA的Key和Cert,会提示设置私钥密码:

1
2
# 生成10年有效的rsa 2048位CA证书(rootCA.crt)与私钥(rootCA.key)
openssl req -x509 -sha256 -days 3650 -newkey rsa:2048 -keyout rootCA.key -out rootCA.crt

1.2 Server证书与Client证书

先生成证书私钥和CSR。生成CSR的过程中会提示输入相关信息,其中的Common Name通常是填完整域名——比如买了 somedomain.com ,如果是给子域名 blog.somedomain.com 发证书,Common Name写的就是 blog.somedomain.com 而不是 somedomain.com

Read More

Archlinux做路由器,并提供透明代理

背景

自从买了macbook air m1之后,家里的台式机就很少用了。以前用来挂机刷碧蓝,但是现在也不怎么玩碧蓝了,所以就改成了做 frp。后来恰逢联通和广电整蛊,我气不过决定换成电信的宽带。师傅装好宽带之后,我看着光猫上3个LAN口(1个接了无线路由器)+1个IPTV口陷入了沉思——为什么不试试把台式机直接接光猫呢,这不就可以让它不占用路由器的WAN口带宽了吗?当然可以的,台式机也成功从光猫获得了IPv4和IPv6地址 [1] ,而且一切服务都正常。

我又想,既然在这个架构下台式机和无线路由器是平起平坐的,那么为什么不简化一下,让无线路由器只负责提供无线功能,路由功能由台式机负责呢?这样做的好处就是可以x86软路由玩起来,花样很多。所以下一步就是……心心念念的,我(台式机)做master,家里其他设备做slave了(大误)。正经地说,软路由透明代理就是为了让PS5和Switch能够无感地加速下载,特别是让PS5可以正常地上传截图。

当然,这样的结构只靠台式机主板的一个千兆网口是不行的,还需要另一个网口。如果用传统的路由器上的接口命名来称呼的话,台式机主板的网口用来做WAN,连光猫的LAN口;而另外买一个PCIE网卡插在主板上所提供的网口就是LAN口,连无线路由器的WAN口。示意图如下:

由于本人学艺不精,在调试软路由+docker+透明代理的时候失去耐心,所以直接把docker砍了,nextcloud根据 archlinux wiki 重新部署了一次——nftables真香。同时,虽然archlinux的wiki建议不要在路由器上提供网络应用(nextcloud),但是我没钱再搞一台电脑也没精力研究用虚拟机做路由器的技术,所以不听老人言吃亏在眼前导致透明代理不可以处理台式机自身产生的流量,否则在v2ray的日志里就会大量出现访问127.0.0.1:443的请求,导致无法使用nextcloud服务。

综上所述,本文会介绍:

  1. x86软路由(开启DHCP),用nftables实现路由和防火墙功能;无线接入点由Bridge模式的无线路由器提供
  2. nftables劫持内网DNS流量至软路由的53端口,由cloudflared发送DoH查询(解决DNS污染问题)
  3. nftables实现透明代理,代理服务由V2Ray提供

第2、3步都是独立可选的,只不过是软路由最经典的玩法。注意下文中用尖括号( <> )括起来的内容都要换成你自己的环境里实际的数值。

1 实现上网功能

Read More

Archlinux下的Python-pip安装需要多做的一步

今天在自己家台式机上安装supervisor的时候,supervisor报错:

1
2
3
4
5
6
7
8
9
10
Traceback (most recent call last):
File "/usr/bin/supervisorctl", line 33, in <module>
sys.exit(load_entry_point('supervisor==4.2.5', 'console_scripts', 'supervisorctl')())
File "/usr/bin/supervisorctl", line 22, in importlib_load_entry_point
for entry_point in distribution(dist_name).entry_points
File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 969, in distribution
return Distribution.from_name(distribution_name)
File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 548, in from_name
raise PackageNotFoundError(name)
importlib.metadata.PackageNotFoundError: No package metadata was found for supervisor

已经在用pacman安装了supervisor,但是package not found,所以试试pip install supervisor,结果居然:

1
2
3
4
Traceback (most recent call last):
File "/usr/bin/pip", line 5, in <module>
from pip._internal.cli.main import main
ModuleNotFoundError: No module named 'pip'

但是也已经在pacman安装了python-pip。Google一下,获得解决方案:

1
sudo python -m ensurepip --upgrade

这就安装好了pip。随后用pip安装supervisor:

1
sudo pip install supervisor

Read More