Recent Posts

AlisterTT's avatar

可算折腾入门了AE

AE算是我上大学时期的的一个噩梦,当时用小破本渲染视频都是整夜整夜的挂机,所以根本就不想碰这软件,最多也就会找个现成的模板改几个字凑合用那种。当时用了邪道的motion,基本是秒渲染,移动几次摄像头就可以搞出很厉害的效果。

随着近几年PC的性能提高,AE的教程增多,上手起来也变得没那么困难,上午看了B站一个教程,26分钟,基本就对里面的插件有了一个概括的了解,试着做了一个demo,自我感觉良好。

's avatar

Element UI 中实现小图片组件展示缩略图,同时大图预览展示原图

(抱歉,想不到更好的标题了)

我们的某个 Vue 2 项目用到了 Element UI,而且很多地方需要展示图片列表,还需要点击图片就能实现大图预览。

Element UI 中提供了 图片组件,而且自带了一个预览功能。但是有一些问题:

  • 直接把所有的原图塞到图片组件,浪费客户端和我们 OSS 的流量。
  • 尽管可以给每个图片组件指定大图预览列表(preview-src-list),但是只有图片组件的 src 跟大图预览列表中任意一项完全相等,才会展示出一组图片中正确的一张,而不是永远展示第一张。
  • 不能靠只给预览列表传一张图的方法解决,因为人家希望在大图预览中能够切换图片。

而解决方案很简单,就是给每个图片组件实时单独生成一个大图预览列表。

<template>
  <div>
    <div v-for="(src, i) in imageList">
      <el-image
        style="width: 100px; height: 100px"
        :src="src"
        :preview-src-list="getPreviewList(i)">
      </div>
   </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component
export default class extends Vue {
  // 缩略图列表
  imageList = ["1-small.jpg", "2-small.jpg", "3-small.jpg"]
  // 大图列表
  previewList = ["1.jpg", "2.jpg", "3.jpg"]

  /**
   * 获取大图列表
   */
  getPreviewList(index: number) {
    const list: string[] = []
    let i = index
    while (i <= this.previewList.length - 1) {
      list.push(this.previewList[i])
      i++
    }
    i = 0
    while (i < index) {
      list.push(this.previewList[i])
      i++
    }
    return list
  }
}
</script>
Phoenix Nemo's avatar

修复 LVM XFS 的 Input/output error

某服务挂了。

设备被强制重启之后发现 LVM 满了,但是文件无法访问,所有文件操作显示 Input/output error

HY's avatar

对开箱抽卡保底系统的一点讨论

现在越来越多的游戏开始加入了开箱抽卡系统,用作游戏的主要营收方式。 这里我们先不提及开箱抽卡作为赌博的本质,单 […]
's avatar

More Than a Yuri Story: Thoughts on Heart of the Woods

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.


Characters in the title screen: Abigail (left), Maddie (right)

Heart of the Woods looks like a nice interesting love story about 2 girls, one human, one ghost. It’s homo, so it’s just what I needed to escape from the real world. Although it appears to involve love across the living and the dead, which is a concept that’s been done to literal death. But hey, the game is well-received, it’s on sale, the artstyle is new to me, and most importantly, it doesn’t appear to be too long. So I guessed it’s going to be a nice fit for the spare time I currently have.

Boy was I wrong. It does not use the old plot device of "one grows old and die while the other stay for eternity" or "the border between life and death is unbreakable so we can’t be together" that human-ghost relationship stories tend to have, which I imagined to also be a leading aspect of this one, too. Heart of the Woods has so much more than that.

Note:

There is no spoiler beyond the first chapter.

The opening scene, Tara (left) and Maddie (right) lie on the train seats

The premise of the story involves a YouTuber that talks about paranormal events, Tara, and the assistant, Maddie. The two travels to a supposedly-haunted town to investigate for their show, because a local of the town as well as a viewer of the show, Morgan, invited them. The ghost appears much later in the story, and is named Abigail (feels pretty unimaginative for a ghost name, to be honest).

At this point, even though none of the interesting things have surfaced. I was quickly drawn in by the sheer beautiful writing. Maddie’s opening monologue very effectively introduced a realistic situation: Maddie has been a long-time friend to Tara, and an assistant to her paranormal events show, but Maddie was going to quit and chase different life goals after this trip to the haunted town. Her guilt, her internal struggles and worry for Tara and all the complex emotions was very expressively shown. I felt I could be in Maddie’s shoes. Through her interactions with Tara, I could feel the awkwardness between them. Tara is salty, Maddie is guilty, but they also know they should keep their cool, but at the same time it’s also really hard to get over their emotions, however they still have a final episode to make…​ This kept going for a long time, it felt real.

It felt funny that I came to this game because of the official pair of Maddie and Abigail, but before that main course came, I was already fascinated by the relationship of Maddie and Tara. I was already worried about them. As Morgan teased them with paranormal phenomena, Tara was hyped and ready to believe, while Maddie was skeptical. Maddie thought Morgan was maniac for claiming crazy things but can’t prove them, and for her infame among townspeople. I actually shared Maddie’s opinion for a while, even though as a reader, I know for a fact that there’s a ghost and so Morgan is at least partially right.

Throughout the former half of the story, the situation keeps getting more complex, and the stakes keeps getting higher. Even without half of the official pair (Abigail) appearing, the 3 characters already seem so interesting. I was just expecting a sweet yuri story, but as I read it, it becomes full-on suspenseful. The suspense peaked at the end of the first chapter, but it did not just decline after that. Things keeps changing, and there is little to no filler texts, no boring segments about "daily lives".

I really liked the romantic parts of the story as well, they complement with the suspenseful parts perfectly. With such good writing, the characters are fleshed out, and their thoughts feels grounded. Their relationships feels human. It’s lovely. With no spoilers, allow me to say that there are lots of hugs, cuddles, and kisses. I felt them, embarrassingly. Even though I’m usually not a fan of erotic scenes in visual novels, I would probably sit through them if this game had any. (There is actually an R18 patch, I didn’t know about it.)

An interesting thing I liked about the character settings is about Tara. The first thing I noticed about her is the voice acting. Her voice sounds unexpectedly deep, almost like…​ the voice a transgender person could make. I thought "it can’t be", but I looked at the credits, and apparently Studio Élan did find a transgender voice actor, and Tara did mention in one line of dialogue that she is trans. The impressive thing is that nobody in the game ever cared about Tara’s voice, even ones that are unfriendly. Tara being trans is not part of the plot and only briefly mentioned. I think this is a massive "trans rights!" shout of how they are just regular people being themselves and we don’t make dramas on their gender. I’m behaving worse than the characters in the game because I’m making a drama about it in this paragraph.

A guest art by Roarke taken from the game’s guest art gallery. I died as I see this.

Let me hope that this article has made you give some interest to the visual novel. It is sold on itch.io and Steam. Ultimately, I think my experience with Heart of the Woods proves the point in my previous article, how good a story feels comes from subverting expectations. This novel has subverted mine a lot, and hopefully I have not spoiled it too much to let it also subvert yours. 🐁


Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

RecursiveG's avatar

手动硬盘安装 WePE

最近尝试了一些 Windows 下的全盘备份/恢复方案,于是顺便折腾了一下各种 WinPE 系统。WinPE 简单来说就是一个 Windows 的 LiveCD,带有各种用于 Windows 的工具。网友们在微软的 WinPE 基础上加入各种驱动和方便使用的图形化操作界面,作为不同的 WinPE “发行版”发布,微PE(WePE) 是这些“发行版”之一。

WePE 自带的安装程序除了安装必要的启动项以外还会安装一些没啥用的选项。所以记录一下手动安装启动项的方法。环境为 Windows 10 64位 UEFI 启动。你需要先制作 WePE 的 ISO,然后把 ISO 里的WEPE目录复制到随便一个分区里,我假设是D:。用管理员身份执行以下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bcdedit /create /device
# 你会拿到一串 GUID, 假设是 {A...}
bcdedit /set "{A...}" ramdisksdidevice partition=D:
bcdedit /set "{A...}" ramdisksdipath \WEPE\WEPE.SDI

bcdedit /create /d "WePE" /application osloader
# 你会拿到一串 GUID, 假设是 {B...}
bcdedit /set "{B...}" device "ramdisk=[D:]\WEPE\WEPE64.WIM,{A...}"
bcdedit /set "{B...}" osdevice "ramdisk=[D:]\WEPE\WEPE64.WIM,{A...}"
bcdedit /set "{B...}" path \windows\system32\boot\winload.efi
bcdedit /set "{B...}" systemroot \windows
bcdedit /set "{B...}" nx OptIn
bcdedit /set "{B...}" pae ForceEnable
bcdedit /set "{B...}" detecthal Yes
bcdedit /set "{B...}" winpe yes
bcdedit /displayorder "{B...}" /addlast
bcdedit /timeout 3

设定完启动项以后就可以把盘符删掉了。

SgDylan's avatar

Imitate DSEE HX

两年前的坑 Remake,大概是最接近 DSEE HX 的实现。

's avatar

自建云游戏服务的尝试

为什么?

我最近经常玩《原神》,但是我:

  • 只有一台能带动这游戏的远程工作站,我还很难物理访问它
  • 平时在外面只能使用弱鸡的旧款 MacBook Pro
  • 用 iPhone 12 Pro 玩,感觉体验堪忧

显然,再为这么一款游戏而专门购买游戏本是不划算的,而且游戏本有着笨重、噪音大、外观普遍中二感爆棚、电池续航几乎不存在等缺点。

考虑到近年来 StadiaGeForce Now 等云游戏服务在国外的兴起,我有了新的主意:拿自己的远程工作站搭建云游戏服务。

本博文不是真正意义上的教程,但是记录了我对若干方案的尝试和使用体验。

环境

  • 服务端(工作站)和客户端都在重重 NAT 之下
  • 有一台阿里云 ECS 做跳板机,安装有:
    • Wireguard(用于和不同的远程设备连接)
    • nginx(通过 stream 模块向公网转发端口)

RDP

在此之前,我经常会使用 RDP 来连接我的远程工作站。启动 RDP 服务不需要复杂的服务端设置,在网关机上也只要简单转发一个端口到公网即可。

其实 RDP 对于一般的应用程序是没有问题的,但是完全不适合打游戏:

Moonlight

我的工作站配备有 GTX 1650 Super,因此可以借助 GeForce Experience 的游戏串流(Streaming)功能和 Moonlight 客户端,轻松搭建自己的云游戏服务。

总体来说,在有合适的网络条件下,Moonlight 可以提供足够好的游戏体验。但是它也有一些蛋疼的问题:

  • 配对时必须在远程主机上输入四位数字确认
  • 一旦连接过 RDP(因为我平时还要工作!),串流服务就会停止工作
    • 所以,上述的配对过程也不能在 RDP 进行,必须使用其它的远程桌面服务(如 Teamviewer);
      同时,每次连接过 RDP,都需要用 Teamviewer 再连接一次。

Parsec

Parsec 是一款与 Moonlight 类似的应用,但是解决了 Moonlight 的很多痛点:

  • 验证是基于他们自家帐号系统的(比在远程主机确认配对方便多了!)
  • 连完 RDP 以后可以直接连接
  • 灵活的连接方式,可以自动视情况通过局域网、Wireguard 等方式连接到服务端
  • 支持 A 卡(虽然我现在用的是 N 卡)

不过,你只能使用 Parsec 他们家的帐号系统,而且他们的客户端比较黑箱。我不是很在乎就是了……

让 GPU 一直工作

我在淘宝上随便买了个 HDMI 锁屏宝,解决了这个问题。

不过,也可以试试 虚拟显示器

对比

总之,不同的连接方案有着它们的优缺点,所以还得视场合选择使用哪种方式连接到我的工作站。

服务/特性 RDP Moonlight Parsec
支持显卡 N/A Nvidia 各种
配置难度 中等
首次连接 输入本机登录信息 在服务端完成配对操作 输入 Parsec 登录信息
流量消耗 较高 中等
图形渲染位置 客户端 服务端 服务端
针对游戏优化
使用 RDP 后再连接 N/A 需要其它远程桌面工具再连接 可直接连接
自动 NAT 穿透
剪贴板共享
开源软件 客户端开源

流量费

别人花 648 抽优菈,我花 648 让游戏跑起来。

黑狗布雷特's avatar

俺说游戏Season2——我十分中意现代3A游戏!

氦,新的一期俺说游戏音频节目正式上线啦!新的俺说podcast以第二季的形式回归了!本期俺说游戏阐述了我对当今流行的开放世界装备驱动打据点3A游戏情有独钟的理由——它甚至涉及到US特遣部队一项关键的军事变革!
Horo's avatar

Google 的 FLoC 是个糟糕透顶的主意

翻译自电子前哨基金会 (Electronic Frontier Foundation) DeepLink BLog 的 Google’s FLoC Is a Terrible Idea : https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea , 原作者 Bennett Cyphers

EFF 网站的内容以知识共享 署名 4.0 协议授权,本译文也如此。

2021 年 4 月 9 日追记:我们开设了一个名为 "Am I FLoCed" 的网站,如果汝有在用 Google Chrome 浏览器的话,汝可以来看看汝有没有“幸运的”成为 …

's avatar

违背期望

在 RSS 中无法显示完整的格式,不同的 RSS 阅读器所显示的内容也可能会有很大差距。为了能够获取到完整的内容,最好还是点开网页链接看。


我觉得影响自己对一篇艺术作品印象好坏的很大的一个因素,是它与我的期望中有多不同。这个艺术作品可以是文学的,绘画的,或者音乐的。

如果一部作品使我对它产生了很高的期望,不管是通过宣传材料还是它的作者(们)(之一)是一位曾经创作过优秀作品的人,那它都需要至少达到、或者超越这个期望值以使得它不显得“令人失望”。这部作品可以客观上很优秀,但是对于我来说,因为它比期望值要差,就有可能感觉好像它真的就是差一样。

同样的,如果我没有对一部作品有很高的期望,比如可能是一部来自从来没听说过的作者的预算比较低的作品,或者是看起来就不是很认真的作品(像是喜剧或迷因)。这时只要它最后比这个低的期望指要做得好,那我就会感觉它好像很厉害,尽管实际上它可能最多只能称作是“普通水平”。

还是来给一些例子吧。被新海诚导演的电影《你的名字》,有一位声望很高的导演、惊艳的宣传海报,并且在我观看之前还看到了非常多的来自其他人的夸奖。其结果就是对它的期望漂浮到了天上。然后在我观看完毕后,感觉到的却只是“不错”,这部电影真的值得那么多赞扬吗?这个问题不好回答。剧情的发展感觉有太多的巧合了,主角们的经历感觉更像是编剧为它们铺好的道路(当然,实际上也确实是这样),而不像是使人信服的冒险。所以在我刚刚看完电影的时候,只给了它 3.5/5 星的评价。我感觉这就是“被高估了”所代表的意思。但仔细想一想,《你的名字》是个精彩的电影吗?大部分人应该都会认同,我也在仔细想了想之后认同了。

在相似的领域,也是一部动画电影,《刀剑神域:序列之争》是相反的情况。《刀剑神域》这个系列可不以有深度的故事而闻名。实际上,很多评论家都觉得它很肤浅,只适合看看爽快。诚实说,我喜欢看《刀剑神域》,应该说,我觉得看看没有深度的爽快很开心。于是我去看了电影,然后电影也提供了刚好我所期望的——没有深度的爽快。实际上可能还稍稍超过了一点,因为电影的视觉效果要比我所习惯了的动画的视觉效果要更加华丽。甚至感觉在看完这部电影后比看完《你的名字》后还要感觉更好,但认真地说,两部作品可不会被我放在同一个级别。

有一部神奇的作品,恰好因为这个神奇的原因使我喜欢,它是《One Room》第一季。是一部共有 12 集,每集 5 分钟的动画系列。它展现的是 3 位少女(不是在一起出现的,而是每位各有属于自己的 4 集)对着镜头说话,像是在和观者在一起渡过时间一样。动画的创作者们称它为“第一人称动画”。这个点子听起来很神奇,不过从上面的描述应该可以看出,应该会是一部看起来会令人感到尴尬的作品。我显然没有对它抱有很高的期望,既然只是动漫美少女对着沉默的镜头单方面说话。只是它反正只有 5 分钟,为了这个神奇的点子,即使它就是很烂,每周浪费了 5 分钟也可以接受。前 2 位角色(前 8 集)的故事同期望中一样,有一点趣味但实际上还是比较无聊,不过没有关系,反正每集只有 5 分钟,再继续浪费 4 次这样的 5 分钟也没有关系嘛。每一位角色有着属于自己的角色歌,被作为片尾曲使用,这些歌曲还可以。

剧透提醒:

以下 1 个段落包含对 One Room 第一季第 9 到 12 集的剧透。尽管你可能不觉得这是一个值得担心剧透的作品,但我写文章的方式应该已经表示了这最后 4 集是出乎意料的。也许可以值得看一看,反正每集只有 5 分钟嘛。 点击此处显示剧透

这一次,歌曲的感觉完全不同了。它现在有了灵魂。在它之后的故事尽管很简单,但却出乎意料的引人入胜。我无法相信自己居然在被这样一部看起来很尴尬的动画打动。如果《One Room》在一开始宣传自己是一系列感人的故事的话,那它一定不会有这样的效果。

我的这个行为——按照一部作品如何违背期望来决定这部作品是否喜欢——大概是非理性的。如果能够更加客观的看待作品,那一定会更好,对吧?我觉得这个做法一定不奇怪,许多人应该也同样做。

不过有时大概这种行为也是有益处的,如果许多人这样的话,我猜。像比如,这样可能会给新出道的作者更多的机会,因此大概会鼓励创新。尽管对于承受高期望的人来说应该反而产生了更多的压力。

Valve 的电子游戏系列《半衰期》是一个有趣的例子。它的第一部游戏(1998 年)是一部经典,震惊了许多人,甚至也包括了在约 20 年后才玩它的我。然后它的续作《半衰期 2》在 2004 年众望所归地发布。人们有很高的期望,这个时候应该也是最害怕它实际上令人失望的时候了。但 Valve 显然又一次震惊了它的观众们,展现了许多新奇的东西。而如果它们只是维持了现状的话,这些新奇的东西一定不会出现。我记得有见过一篇对游戏团队领导的采访,他说,震惊玩家与取得突破正是他们所努力争取的。开发者们对着自己期望很高,才得以匹配消费者的期望而成功。但是这样高的期望——依我看过各种信息所理解的——也是它们的弊端。《半衰期 3》的开发渐渐停止,最后也一直没有完成,大概是因为开发者与消费者们对它的期望都太高,超过了实现的能力吧。

所以……嘛,我也不知道该从这篇文章的讨论中总结出什么,只是想突然无端地说一下。所以那么,希望这篇文章有一点价值,感谢阅读吧。 🐁


题图取自 One Room 第 1 季第 9 集的 0'23" 。

除特殊说明以外,本网站原创内容采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。转载时请注明来源 FiveYellowMice 的博客 ,以及原文链接

's avatar

Subverting Expectations

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.


I think a huge factor in how good a piece of artwork feels to me is how much it subverts my expectations. The "artwork" in question can be literature, illustration, music or perhaps more other types.

If a work sets up a high expectation from me, by either making really good marketing materials or its author(s) is/include someone who has produced good works before, it will need to reach or overachieve for it to not be "disappointing". It can still be objectively really good, but for me, because it is comparatively worse than the expectation, it may feel like as if it’s actually bad.

Likewise, if I don’t have much expectation for a work. For example, a low-budget work from someone I have not heard of, or works that does not appear to be serious (like a comedy or a meme). As long as it turns out to be better than that low expectation, I will feel like it’s amazing. Even if objectively, it’s so-so at best.

I want to give some examples. The movie Your Name directed by Shinkai Makoto. It has a highly-reputed director, stunning marketing posters. And before I watched it, countless praises had been seen from my peers. The resultant expectation was floating to the skies. After I watched it, I felt it was "not bad", but was it worth the hype? That was hard to answer. The plot progression felt like there’s too many coincidences, the main characters' experiences seem to just be a road laid down by the writer (which to be fair, it obviously is) rather than a believable adventure. So right after I finished the movie, I gave it 3.5/5 stars. I felt this was exactly what "overrated" means. But let us think rationally, is Your Name a good movie? I suppose most people would agree, and I agreed after I thought rationally.

On a similar vein, also an anime movie, Sword Art Online: Ordinal Scale, but this one faces the opposite scenario. The Sword Art Online series is not known for it’s depth. In fact, its story is considered really shallow and only good for "thrills" by critics. To be honest, I like Sword Art Online, I mean, I like plain thrills without much depth. So I watched the movie, and it offered me exactly what I expected: thrills without much depth. It actually overachieved a little because the movie has flashier visual effects than what’s in the TV series which I was used to. I somewhat felt better after watching this than after watching Your Name, but I would not put the 2 works on the same level.

There is a work I surprisingly liked because of this weird reason. That’s One Room season 1. The 12-episode 5-minute-each anime series that shows each of 3 girls (they don’t appear together, each one has 4 episodes dedicated to one) talking to the camera as if she is spending time with the viewer. The makers call it "first-person anime". It felt like a novel concept, but as you may guess from its description, it’s going to be an awkward anime to watch. I obviously didn’t expect much from stereotypical anime girls talking to a silent camera. I just watched for the novelty since even if it was terrible, I only lose 5 minutes each week. The first 2 characters (8 episodes) was as I expected, kind of sweet but still boring, but it only lasts 5 minutes so I didn’t mind wasting those 5 minutes 4 more times. Each character has a song sung by the voice actor and used as the ending song, the songs are ok.

Spoiler Alert:

The following 1 paragraph contains spoiler for One Room season 1 episode 9-12. Although you may not think spoilers for this work is worth worrying about, the way I wrote has indicated that those episodes are unexpected. They’re probably worth a watch, they are only 5 minutes each anyways. Click here to show spoilers

This time the song felt vastly different. It has a soul now. The story behind it, while simple, was unexpectedly engaging. I could not believe I was moved by a show this awkward-appearing. If One Room had marketed itself as a collection of nice stories, it would not have this effect.

This behavior of mine, using how are expectations are subverted to decide whether to like an artwork, is probably irrational. It’s definitely better if I can view works more objectively, isn’t it? I assume this is not weird and many other people do the same.

There may be situations where this subversion-seeking behavior is beneficial, if many people have it, I guess. Like maybe it gives more chance to start-up authors than to established ones, which perhaps encourages innovations. Although it probably makes life harder for people under high expectations.

The video game series Half-Life by Valve is an interesting example. The first game (1998) is a classic, people were astonished by it, including me who played it at about 20 years later. Then the long-awaited sequel Half-Life 2 came out in 2004. People then had high expectations, and that’s probably when the fear of it being a disappointment was the highest. But Valve had apparently once again astonished the audiences by introducing so many new things that would definitely not exist had they been keeping their status quo. I remember reading about an interview to the games' leader, and he said astonishing people and making new breakthroughs was exactly what they worked hard for. The developers expected very high from themselves to match the expectations from consumers in order to succeed. But this high self-expectation was, as I understand it from reading various information, also their detriment. Half-Life 3’s development slowly halted and never completed, probably because both consumers and developers expected so high that they were unable to fulfill it.

So…​ Well, I don’t know what to take away from the discussions in this article, I just wanted to spontaneously talk for no apparent reason. So I hope this is informational, and thanks for reading. 🐁


The heading picture is taken from 0'23" of One Room season 1 episode 9.

Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

Phoenix Nemo's avatar

FAT32 与 FAT32 的不同

缘由:游戏用的台式机坏掉了。

HY's avatar

策略性游戏的随机性

这么多年策略性游戏玩下来给我印象最深的不是某些大公司的作品,而是一款独立游戏《Into the breach》 […]
ホロ's avatar

用 Hyper-V 玩些小花样

从前面那篇 Hyper-V 和 OpenWrt 搭建软路由 开始咱就开始在 Windows 上用 Hyper-V 了。 然后场面逐渐失控……

这篇文章就是把咱这几天遇到的各种问题和可能的解决方案记下来这个样子。

汝应该尝试 Hyper-V 吗?

如果汝有像咱一样的某些需求的话,那 Hyper-V 可能也很适合汝。

  • 想在 Windows 开机时自动启动某个虚拟机。

    VMware Workstation 15 还可以用内置的共享虚拟机设置开机启动的,不过 16 开始弃用了。 VirtualBox 文档里有提用服务设置开机启动虚拟机。 虽然咱一次都没成功过……

  • 有用另一台电脑访问这台电脑上虚拟机的需求。

  • 在用 WSL 2,那其实汝已经算是启用了 Hyper-V 了嘛。

  • (或者还有别的?)

不过汝也许会因为这些原因不想尝试。

  • 旧版 VMware Workstation 或 VirtualBox 用户。

    启用 Hyper-V 以后会导致旧版 …

's avatar

Making a Custom Smart LED Clock

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.


The finished product displaying a time of Tuesday, 16 March 2021 13:37:07

After around 2 years, my first (presentable) DIY project is finally complete! It’s a device with a 16x16 LED matrix display, which is primarily used to display time. It’s also WiFi-connected, syncs time via NTP, can have 3 alarms, and potentially do more interesting stuff with its internet capability.

So, this post will document the process of me making this thing. I didn’t have many mishaps, the process is relatively smooth, and the reason it took so long is mostly me procrastinating.

For a lack of name ideas, I used a random pet name generator online, and it gave me the name "Baby Tiger". This is probably not the best name for a digital clock (it sounds like a very generic name for a cat), but I have no better ideas. Name suggestions are welcome.

The Electronic Components

I guess it’s because of watching YouTubers like GreatScott!! and DIY Perks piqued my interest and I wanted to make something of my own. And a clock with LEDs sounded like a fairly conservative thing to make that would not present much trouble, since similar hardware and design patterns seems to be done by others a lot. So with this thought, and the knowledge from YouTube videos that a kind of microcontroller⁠[1] called the Arduino would be suitable for controlling LEDs, I bought an Arduino Nano knockoff⁠[2], breadboard⁠[3], jumper wires and RGB LEDs.

The first RGB LED I got

This RGB LED is a package of 3 LEDs of colors red, green and blue. I need to power each color individually in order to display different colors. I made it to work, but there were big catches:

  1. I can’t adjust the brightness of each color components directly, so I can only have the primary and secondary colors, plus white and black (off). I had to use PWM⁠[4] in order to make fancier colors.
  2. The weirder problem was this RGB LED is unlike just 3 LEDs in parallel, the red, green and blues LEDs can’t be on simultaneously, which means I had to do PWM faster and the software needs to be a little more complicated.
  3. Each LED consumes 3 pins for the 3 color components. I was making a clock, so there eventually needs to be a large quantity of LEDs connected before displaying anything resembling time. There aren’t that many pins on an Arduino Nano. I learned about shift registers that can "expand" the number of controllable pins, but then I had to do PWM with software which felt very complicated. Shift registers will also make outputs slower, combined with the issue above, I was afraid that the speed of an Arduino Nano wasn’t fast enough.

I complained about this online and prepared to buy some shift registers to try nevertheless, then someone online suggested me to take a look at a more sophisticated RGB LED called the WS2818B. I looked it up, and it seems like a very popular choice of RGB LED. With a WS2818B, it will only need a single data pin to receive the color data, and will retain the color without the controller doing PWM. It also appears that those things can also be chained so I can have theoretically as much LEDs as I want, using just a single pin to transmit color. These features compared to the original LEDs, sounded like driving a car compared to crawling. I was carried away, and thought of expanding the project into something fancier.

I won’t make any regular old digital clock, I wanted make it to have a matrix of LEDs that functions as a screen, so that it can display more interesting things than just time. Coincidentally, I saw a product listing of 16x16 WS2818Bs pre-made into a matrix, which sounds like exactly what I want, saves me the trouble of laying out the total of 256 LEDs and soldering them.

The LED matrix, taken from the product listing web page

As I researched, the device won’t function with just a microcontroller and 256 LEDs. It will also need these less important, but still necessary things to be a decent clock:

  • A power supply. Both the Arduino Nano and the WS2818B matrix wants 5V power. According to an article I read online, a single WS2818B LED can draw 50mA of current, which sounds small. But I have 256 of them, so it multiplies to 12.8A. I couldn’t find a mains power supply that converts to 5V and is rated for more than 12.8A. What I was able to find were a mains to 12V power supply that can carry that much power, plus a converter that converts those 12V into 5V. The 12V-to-5V converter can only carry 10A though, but according to the article mentioned above, 50mA is just a maximum, so as long I don’t run it at full brightness and every pixel being white, it should be fine.
  • 5.5mm DC power barrel jack. This allows the 12V power supply to connect to the input of the 12V-to-5V converter.
  • DS1307 RTC module. This module keeps the clock running in the background, even when power is disconnected, so I would not need to set the time every time power is reconnected. I suppose it may also a bit more accurate than counting the time with the Arduino’s internal CPU clock.
  • More wires and breadboard.

At the same time, I came across something called an ESP8266. They say it’s similar to an Arduino, but it can connect to WiFi. I then excitedly decided to use that as the central microcontroller and ditched the now-dumb-looking Arduino Nano, and started daydreaming about the potentials of having internet access: I could synchronize time from the internet, and possibly displaying messages and notifications, even stream videos! (Yes, I know, playing a video on a 16x16 screen is extremely impractical. It could probably play Bad Apple though, which sounded pretty cool to me.)

Now the project has become more ambitious, I started to think of even more features that I could add to this clock.
Displaying current temperature? Yes, let’s add a temperature sensor.
Having the ability to sound alarms? Yes, let’s add a buzzer.
Buttons too small and ugly? Let’s use the 4 Cherry MX key switches⁠[5] I happened to have lying around instead of regular small tactile buttons, and use some custom key caps which I’ll worry about later.

So here are the aforementioned things I ended up having inside the clock, plus some other details:
(This photo was taken after the whole thing has been built, it obviously didn’t look like this at that stage. They were stuff plugged onto breadboards, I have a photo of that later in the article, but that photo wasn’t suitable for explaining what the components are.)

① 12V-to-5V DC converter
② 5.5mm DC barrel jack
③ Arduino Nano
④ ESP8266
⑤ DS1307 RTC module
⑥ BME280 temperature sensor
⑦ Buzzer
⑧ 5V-3.3V bidirectional logic level shifter, this allows the components operating at 3.3V (ESP8266 and BME280) to communicate with the components operating at 5V (others) without blowing up
⑨ The board that holds the 4 keys of the device, a.k.a. key board, not to be confused with a computer keyboard
⑩ Where the key board connects to the main board
⑪ Where the 16x16 LED display connects to the main board, the display is on the other side (front)
⑫ Unused connections of the display glued to the box so they don’t get in the way
⑬ Unused connections of a reset button, I decided later that this is unnecessary for normal use
⑭ Unused hole where the reset button was supposed to be

Why do I still have an Arduino Nano in the picture above? Didn’t I say I ditched it? Well, it turns out that controlling 256 LEDs with 1 pin requires a lot of work from the microcontroller. It requires very precisely timed signals so the microcontroller can’t do anything else during signal transmission, which is bad if I want to have WiFi connected because it requires some WiFi stuff to be constantly done, I also want time for my own code to run. There are tricks to use dedicated hardware to transmit these signals, but they either consume too much memory, or occupies the pin I need for other purpose. So I put the Arduino Nano back, and programmed it so that when the ESP8266 wants to update the display, it transfers the display data into the Arduino Nano, the Arduino Nano then does the time-consuming job of transmitting precisely-timed signals, while the ESP8266 carries on doing more important things. Was is a waste to put a whole Arduino just for this singular purpose? I suppose so, but it was the easiest way of solving my problem.

The Software

After I have got those components on the breadboard and connected (it didn’t look like the picture above, it was just on the breadboard), I started programming the software.

First, I decided the interactions I could have with the clock. It will have 4 buttons: , , , . When it’s powered on, it will display the current time (I’ll call it "clock mode"), pressing will turn off the display, and pressing will bring up the main menu, where the various additional functionalities (like alarms and settings) can be selected. and changes the selection, confirms the selection, and returns to an upper-level menu or the default clock mode.

Flowchart of basic interactions of Baby Tiger

While making the clock mode, I realized that there isn’t enough pixels to display everything about the current time. A 0-9 digit takes at least 3x5 pixels, each 2 digits needs to have 1 pixel between them so they don’t fuse together. It ends up being a little challenging. I have to use different colors for different parts of the time for them to look distinct. I made a separate screen to show the year number and month name when is held down. Pressing will on the other hand switch the time display into temperature/humidity and temperature/pressure obtained through the BME280 temperature sensor.

Examples of the different screens in clock mode

At this stage, I was pretty happy about what I have accomplished, so I posted its photo on social media.

What it looked like after completing the clock mode

Afterwards I made the main menu (and by afterwards I mean 1.5 years later, yes I procrastinated for that long). It shows the name of the menu item at the bottom half of the screen. Right now they are Alarms, Games and Settings. Since the screen is so small, only one option is shown at a time, and the text scrolls from right to left.

Main menu showing the "Settings" option

The settings menu consists of these options:

  1. Time: Setting the current time manually. There aren’t much to be noted, except writing the UI interactions were kind of tedious. Anyways, it works.
  2. Timezone: Because I use NTP to synchronize time with the internet, and NTP only cares about UTC, I have to take care of the local time offset locally, which is changed from here.
  3. NTP: I originally planned to make an On/Off switch for NTP, but didn’t ended up bothering, so right now it just shows the last synchronization time, and if there is an error synchronizing.
  4. WiFi: Setting the SSID and password of the WiFi network to connect to. Since the clock does not have a keyboard to type those things, I have to do it the around-about way: Let the ESP8266 make a WiFi access point for a phone to connect to, present a web page for inputting the SSID and password, receive them from the phone through HTTP, and finally try to connect to the network. Writing code for this process was the most tedious of them all (I wrote an HTTP server from scratch!). It eventually worked, kinda, although I don’t dare looking at the spaghetti code I wrote ever again.

In the alarms menu, I made 3 alarm slots. The enabled alarms are green, while the disabled ones are red. Since I don’t have enough vertical pixels to display 3 rows of timestamps, the last alarm will have its last bit cut off, and everything will get shifted up when the last alarm is selected. Going into an alarm setting (by pressing ) will let me change the hour, minute, on/off, whether it repeats on each day of of a week, and which music to play when the alarm triggers. It ended up looking pretty good, if I do say so myself.

Alarm setting on Baby Tiger. There are 3 alarms, the 1st and 3rd alarms are disabled, the 2nd alarm is enabled and set to 7:15, repeats every Friday, music set to Katyusha.

I originally planned to put some games on there, but they aren’t exactly necessary, and seemed like a lot of work, so in the end I didn’t write any. The "Games" on the main menu is just the demo music player that plays Senbonzakura.

I mentioned 2 musics that’s on Baby Tiger, Senbonzakura and Katyusha. They aren’t really musics though, as I could only play square waves from a ESP8266. It can’t play regular music file, nor does it play MIDI. These 2 tracks are just notes I typed into the code manually. (I really should write a script to convert from MIDI, if I really want many different music to select from. Doing this by hand is pretty time-consuming.)

And then I felt like the software was done enough. So the next step is to figure out how put them in a good-looking enclosure. But before that, I got to move the electronic component out of the wobbly breadboard, onto a more sturdy circuit board.

The Circuit Boards and Keys

So the plan was to have 2 separate circuit boards connected by a ribbon cable. One was going to be the main board, holding most of the stuff; the other was going to be the key board, holding the 4 key switches, as well as the capacitors and resistors for debouncing.

Let’s talk about debouncing⁠[6]. There are 2 ways of debouncing, hardware and software. I chose hardware because the article I read to learn about the topic said "It’s a hardware problem, so it’s best to be solved with hardware." and I agreed that hardware feels more elegant. The problem is, I don’t have an oscilloscope, so I just had to guess how long and how often the buttons bounce, and in turn had to guess the capacitance and resistance values in the debouncing circuit. The guesswork was a weird experience. The idea of hardware debouncing is to let the capacitor charge or discharge slowly when the button is pressed or released, so even if it bounces a little, the voltage would not change too much as to let the microcontroller think as extra button presses. That means the bigger the capacitance, the bigger the resistance, the slower the voltage changes, so the less likely for the bounces to have an effect, with the trade-off of the button press feeling more laggy, right? Well maybe my understanding is wrong, because changing to bigger capacitor apparently make the bouncing worse than having a small capacitor. Changing resistors could also make it worse – or better, it feels completely random, and the ESP8266 is so weird that some pins seems to work the best with different valued resistors than other pins. I still couldn’t understand why that is, maybe the answer will be more apparent if I had an oscilloscope. Eventually though, I figured out a circuit that worked well enough.

Schematic of the key board

I didn’t plan to draw and order a custom-made PCB, I thought just soldering stuff onto a perf board⁠[7] should work well enough for my purpose. I was wrong.

I started out making the key board since it’s the simpler of two. Then I realized what appears to be simple on a YouTube video may not be so simple if I do it myself. Those YouTubers had experiences and I didn’t. The process of realizing the circuit diagram above was slow and painful. I couldn’t keep the components in place before soldering them, it ended up resulting in none of the key switches are seated nicely, they tilted in all different directions. It’s really unideal, but what’s been done is done, I can’t do it again unless I buy new key switches. It was also difficult to make the wires connect to where I want them to connect to while not touching where I don’t want them to have contact with. After I finished making the key board, I concluded: There is no way I can do this manually for the much more complicated main board.

Schematic of the main board

Defeated, I sat back to the front of the computer, and decided to draw a PCB with KiCad. I didn’t know how to draw PCBs, but luckily it didn’t take too much time to learn with the help of guides found online. And I was glad to not go down the manual perf board soldering path, because the PCB I ended up drawing looked like this: They are all necessary connections.

PCB design of the main board, red lines are traces on the front layer, green lines are the traces on the back layer

I sent the PCB design to a company that makes custom PCBs for individuals. The board itself was not very expensive, costing US$ 4, but the shipping though, they cost $14 and was estimated to take about a month. The DHL express shipping costed $21 but is estimated to take less than a week. I chose the latter. The slow shipping was already that expensive, so it felt justifiable to buy the 1.5 times as expensive but 4 times as fast shipping method.

It took less than a week to arrive, as expected from DHL. I then soldered all the remaining components, completed with significantly less sweat than when I made the key board.

Now comes the moment of truth: If I drew the PCB wrong, then things will not work and I’ll have to spend $25 again. Or worse, I’ll blow up components. Luckily nothing seemed wrong as I plugged in power – a successful milestone.

The photo I took when posting on social media

The Enclosure

It was the time for the non-electronic part: making an enclosure for everything to sit nicely and look good in. In my imagination, I figured that the shape of the enclosure should be a rectangular box, with a shallow depth, like a picture frame. The front has the screen, covered with translucent acrylic, and behind the screen are all the control circuits. I figured that wood is probably suitable as its main building material. It’s heavy enough so the device won’t tip over, and it also sounds like a very commonly used material.

I had no experience in woodworking. I was a bit overwhelmed by all the information I needed to get started without spending too much money. There were questions like what suitable types of wood are, how to make right angled joints that aren’t easily broken, what types of glues I need, how to cut square holes, how to make a detachable backplate. A lot of the problems weren’t hard, they seemed to be pretty easy with powerful enough tools. But I didn’t want to spend that much money on powerful tools. They were really expensive. OK, they were not that expensive if I was going to use them all the time, but I couldn’t think of when the next time I use them will be, and investing hundreds of dollars into tools I could only think of using once is definitely not worth it.

To demonstrate my point above, let’s talk about how I wanted to cut 20x20mm square holes onto a piece of wood in order for the keys to poke out. What options did I have? The tools I had were an electric dill (which I bought from Ikea a long time ago) and a hacksaw. An electric drill can only dig round holes, how do I make square ones? The answer I got from the internet was to use a "router". Well, I did have a router to use, it even had 1 Gigabit Ethernet, but it’s apparently not the "router" I need to cut square holes. The router for cutting square holes was something I didn’t have, and costed quite a lot. And apparently, a router was not enough! To assist the router into cutting straight lines, I would also need a "template", and templates seemed to be quite a deep rabbit hole.

I guessed I could first drill a round hole that the diameter is the same as the side length of the square, and then use a saw to cut the corners. Like the left halt of the image below. But I could not find affordable drills bits that are 20mm in diameter, and I could not find saws that would fit into a 20mm round hole even if there were 20mm drill bits.

Circular holes are drilled first, then the material in shaded areas are removed by other means, creating square holes

So sawing were out of question, what else? Maybe using a file instead of a saw? It’s probably going to be more time-and-strength-consuming though, but I was able to find small enough files for not-too-high prices. As for drilling the round holes, I guessed using the largest drill bit my drill could hold (8mm), and drilling multiple of them should suffice. As seen of the right half of the image above.

After painstakingly researching every potential problem I though of (they were not enough), many WikiHow-reading and YouTube-watching. I settled on a design that balanced ease-of-making and looked passable.

The mockup 3D model. From left to right: front, back, back with backplate off

I originally planned to use wood for the square frame (yellow in the 3D model), the display support (orange in the 3D model, put there for the display to be glued on) and the back plate (also yellow in the 3D model). I didn’t expect regular wood to be so expensive though! So I turned to the alternatives, which are still at least made of wood fibres. Plywood, which are made by gluing thin pieces of wood together, and somewhat cheaper. And MDF, which are made by compressing wood fibres (I feel like they are similar to paper, just thicker and stronger. MDF also disintegrates with water, just like paper does.), and quite a bit cheaper. I contemplated on which to use, watched a few videos explaining the differences. It turns out plywood seems to be better because the dust is less fine and thus less harmful for the lung, and does not disintegrate with water. But I still turned to MDF since it’s quite a bit cheaper. It shouldn’t matter much for my project, I guessed.

So I went shopping for tools and materials. I bought:

  • A piece of 12mm thick MDF, very large, much more than I needed, but they don’t sell smaller ones.
  • 2 pieces of 3mm thick MDF, for some reason they had small piece for 3mm, and the size was just enough for what I needed.
  • A large file.
  • A set of small files, they only had small files in a set and not individually.
  • A handsaw.
  • Wood glue.
  • 4 clamps.
  • A square stick of cheap and soft wood for support while gluing and drilling.
  • 8mm drill bit.

Then I started cutting wood into the pieces shown below. The reason I used this shape with protrusions and slots for the side pieces was to increase the surface area of the joints. The 4 side pieces joins end-to-end, and the protrusions slots into the slots. I am not sure if this was worth the effort since this thing doesn’t need to be that strong, but everyone online seems to emphasize how important it is to increase the surface area of joints. And no one on YouTube seems to make a right angled joint by just sticking one end of a piece onto the side of another (a.k.a. a butt joint).

Wood pieces I cut. Black shapes are 12mm thick, orange shapes are 3mm thick. Units are in mm.

I tried to make the slots on the side pieces by sawing off as much material as the saw can reach and filing down the remaining. It turned out the large file I bought wasn’t suited for the job. I filed for a long time but little material were visibly removed. The small files were obviously going to be worse, since they are even finer. Now making the slots were already this hard with the files I have, making the square holes in the later steps were definitely going to be harder. So I went back to the hardware store, returned the small file set, and bought a set of rougher files. Filing was still slow with the rougher files, but at least my work could have visible progress.

Making the square holes was as painful as I imagined. Drilling the 8mm-diameter circular holes were easy with an electric drill. Removing the rest of the material in the square hole area was a long and tiring process. I filed and filed. Working from afternoon to evening to early night. Had my neighbour complaining about noise. The "square" holes were irregularly-shaped. But I was really tired, so I decided it was good enough and stopped for the moment. It later turned out the holes were not large enough to fit the keys, so I had to enlarge them a little after gluing.

The "square" holes as seen on the finished product. Don’t mind the key caps, I’ll talk about them later.

After cutting the 4 side pieces, I glued them together to make the frame. Since the protrusions and slots on the ends of the pieces were not that perfect either, the wood glue did not really make great contacts. But they still held up, which was good enough. I was really scared of the frame turning out to be not square (slanted into a parallelogram), but luckily it was square enough, after waiting 24 hours which was how long the label of the wood glue bottle said I needed to wait.

I also cut the display support and backplate, and glued the former with hot glue.

Since I originally planned the box to be made of wood, I didn’t plan to paint them. Wood grain patterns seemed pretty nice. But now I’m using MDF, I had to paint it so it is not MDF-brown-coloured. The front panel was supposed to be a black translucent acrylic in order for the screen to appear black where pixels were not lit. So I guessed I might as well paint the whole thing black. But also because it was MDF, which disintegrates with water, many paints cannot be used on it, including acrylic paint which was the one of the only paint types I could get in small quantities (I guess most paint containers were designed for people who paint walls and stuff. Buying 2kg buckets of paint just to paint this small thing seemed super not worth it for me.). I went to research online again, and concluded that I should use spray paint cans.

I went to the hardware store once again to buy the spray paint cans. The spray paint cans’ label said I need a primer as well, so I bought a can of flat black paint and a can of primer. Back home, I laid down paper of the floor of my balcony and started spraying. It smelled pretty bad even as I did it on the balcony. It was also a long process, since instructions online as well as the label said I need to wait 15 minutes between each coat. So after painting a few coats of primer and more coats of black paint, it became dark once again. Fortunately I finished before the sky was as dark as I stopped being able to see clearly.

Remember the hot glue I used to secure the display support? One corner came loose after the spray painting. So I added wood glue as it should be stronger than hot glue.

At this stage, I still did not have a way to put the back plate on. It needs to be removable as I need to access the electronics inside. I first thought of using screws, but people said screws in wood becomes loose after unscrewing. A friend suggested using magnets, which I guessed was a good solution.

I bought 5mm diameter neodymium disc magnets online. 5mm is a size that can fit into the sides of my 12mm thick side panels, and also a size I have a drill bit for. I drilled a hole on the middle of each of the 4 sides and placed magnets in them. I also taped magnets of opposite poles onto the corresponding locations on the back plate. There were more problems:

  1. My hand were not still during drilling. So the holes were not in the exact centers of each sides. They drifted a little, enough drift for me to having to adjust the locations of magnets on the back plate. Since the drifts were irregular, I had to lay the frame onto the back plate, poke the magnets into each of the 4 holes, then carefully lift the frame, leaving the back plate with the magnets on the correct location.
  2. I put the poles wrong! Same poles of magnets repel each other which was the opposite of what I want, so I had to take them off, and repeat the step above.
  3. 1 magnet pair on each side turned out to be a lot weaker than I expected (Aren’t neodymium magnets supposed to be insanely strong?). I tried drilling the holes deeper and adding more magnets. Eventually I put 4 magnets on each sides of the frame and the back plate, a total of 32 magnets.
  4. Hot glue could not make a strong bound for gluing neodymium magnets to MDF. I had to buy yet another type of glue: the 2-part epoxy. It came off once more, and had to try again with thicker layers of the glue.
  5. One magnet on one side of the back plate did not get glued well, and got broken off and stuck inside the corresponding hole on the frame. It seems to have stuck pretty tightly, so I didn’t bother getting it out and gluing it again. It became a feature.
Finished back plate, this side faces the inside so I didn’t bother painting, and the hole is for plugging power

Before putting the front panel on, I glued the display onto the display support, also with epoxy glue.

I wanted the acrylic front panel to be translucent, not semi-transparent, in order to diffuse the light from inside so I don’t see the pixels when they are off. The 200x200mm acrylic I bought from the beginning was translucent when the protective films were on, but I knew it would just be a semi-transparent black when the films were peeled off. Leaving a protective film on the finished product is definitely ugly, so I needed another light diffusing layer. I thought tracing paper would be fitting, but they were actually quite expensive and the stores I went to had them sold out. I found a piece of frosted plastic in alternative, which I think actually worked better than if I had used tracing paper. I glued one side of the acrylic to the cut frosted plastic with epoxy glue. And then with the plastic facing inside, I glued them onto the frame. It came off once and I had to glue again.

By the way, 2-part epoxy glue smelled a little gross, the B part smelled like fish for some reason.

With all these stuff done, I could finally put the electronics in. I glued the key board, main board, the 12V-to-5V converter, the DC power barrel jack in this order, with a combination of hot glue and epoxy glue. So far they have stayed in place.

Inside the enclosure after all components have been put in. It’s the same picture from before, ignore the numbers.

Completion

Plugging everything in, now it’s the most important moment of truth. If this somehow ended in smoke, I would be very upset. It shouldn’t have any problems though, as I have tested it in every stage. I was nervous, my hands trembled as I plugged in the power, my heart apparently stopped beating for a second before the display came on. But finally, my worries turned out to be needless, as the product of my hard work blinked each second with accurate time on the screen.

I called it a success.

I posted the completed photos on social media once again, and people congratulated me.

The final results. Left shows the time, right shows the temperature and humidity.

Well, it’s not flawless though. First of all, the whole thing looks totally handmade and imperfect. The surface finish is rough, and marks left by dried glue is all over the place. Some unpainted areas shows from the outside. The back plate does not attach well not only because the back of the frame is not flat at all, but also because the glue around the magnets on the back plate formed a chamfer, preventing them from going all the way in. Unlit pixels still shows though the front panel a little, though I guess it’s acceptable. And not to mention the ugly-shaped corner joints and holes for the keys. Also there’s an unoccupied hole on the left side that was supposed to house the reset button I ditched.

Second of all, the temperature measurement is a lot higher than the actual room temperature. The reason is probably the other components are dissipating a significant amount of heat so the inside becomes hot. I didn’t foresee this, I thought this thing wasn’t going to emit enough heat to affect the temperature measurement, I was totally wrong. The amount discrepancy is unacceptably high (like, about 8 ℃). Also because all the other environmental measures (humidity and pressure) are based on the temperature measurement, they are all unusable. I took off the back plate so it’s a bit better, but, like, there’s no back plate then.

It’s also not really finished. I used Cherry MX key switches, so I got to use key caps designed for a computer. But my buttons are , , and . On a computer keyboard, there are left and right arrows, but obviously no cross and circle. I could use X and O letter keys for and , but they are differently shaped from each other and the arrow keys. I saw online that epoxy resin⁠[8] is something people often use to make custom key caps, so I guess I could do that as well. But that’s another deep rabbit hole I’ll go into later in time. Meanwhile, I just randomly took 4 same-shaped key caps from a pile. They’ll eventually get replaced.

The software also doesn’t have much functionality. The excitement of having WiFi when I switched to ESP8266 did not do a lot. The only thing that currently uses WiFi is syncing time via NTP. I guess I might want to expand the software more later in time, adding countdown timers, stop watches, displaying notifications, and maybe actually playing Bad Apple.

Throughout the making of this project, it made me realize that DIY is probably not going to be a money-saving options oftentimes. The amount of money I spent on tools and materials is something I had lost count on, even though I tried to be as cheapskate as possible. Not to mention the amount of effort and time. I guess if the project is simple, and you have most of the tools, DIYing may be cheaper, I dunno. I wanted a custom digital clock that is not sold anywhere though, so I’d like to think my project was worth it for me.

That’s it. Finally I finished writing this long article which took 2 days to write. I have published all the code and design files, in case you want to know any technical details, or use them as references. 🐁


  1. A microcontroller is a micro electronic component that can be programmed to control circuits. It’s a very simple kind of computer.
  2. An Arduino Nano is basically the same as the most popular Arduino Uno, but smaller and cheaper. A Uno’s size is way too large in my opinion for something with this little processing power.
  3. A breadboard is a mainly-plastic board with holes all over them. One can insert electronics and wires onto it and have them work without soldering.
  4. PWM means turning on and off the LED so quickly that it appears to the human eye as if it’s continuously on but dimmer. Adjusting how much time it’s on verses it’s off changes how bright or dim it appears.
  5. A key switch is what’s underneath the key cap on a mechanical computer keyboard. Although it’s primarily used for keyboards, in terms of how it interacts with electronics, it’s the same as a regular button. I had the 4 key switches left over from repairing my computer keyboard.
  6. Buttons are mechanical construct, in the real world, the mechanical contacts will swing back and forth a bit when they are pushed together or released away. So pressing a button by hand once will be seen as the electrical contact has been closed and opened multiple times thus being thought as multiple key presses. Debouncing is the act of removing the effect of these unwanted openings and closings.
  7. A perf board is something like a circuit board, but with no internal connections and have a matrix holes filling the entire surface (So it doesn’t need to be custom-made). One would insert components on one side and solder and connect them on the other side. Search for images should give you a good idea on what it is.
  8. Epoxy resin is not the same as epoxy glue, although they are similar. Epoxy glue usually sets faster (tens of minutes), have a color tint, and sold in small volumes. Epoxy resin usually sets slower (about a day), are clear, and sold in larger bottles. Epoxy resin seems like a pretty popular material to make artistic objects, like custom key caps.

Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

ホロ's avatar

Hyper-V 和 OpenWrt 搭建软路由

笔记本上虚拟机越开越多,于是怎么给它们划分网络就是一个问题,例如代理软件什么的。

因为咱不想每一个虚拟机上就单独设置一遍,就想起来了咱给咱手上不支持代理设置的设备 (例如游戏主机什么的)连的那个运行有代理软件的路由器了。然后发现已经有人尝试过了,那就开工咯。

介于随便汝等说咱是更想授之以渔还是就是在偷懒都好的想法,咱不会给出事无巨细的操作方法。 当然如果汝也想跟着做的话,遇到问题时也可以来问咱(或者上网搜索一番)。

以及基于 Hyper-V 的特殊性,启用完以后可能会导致老的虚拟机软件无法启动。 嘛最新的 VMware Workstation 和 VirtualBox 应该是已经支持了的。

准备工作 - Hyper-V 管理器、映像和创建虚拟机

要使用 Hyper-V 的话,汝得有一个 Windows 8.1/10 专业版或者企业版, 或者 Windows Server 2012 以后的 Windows Server 才行。同时也要求汝的 CPU 支持必要的虚拟化技术。 (Intel VT-x …

HY's avatar

内容清空,重新开始

一转眼博客已经有1年多没写了。 刚好这个机会清空一下历史文章,重新开始。 也不知道这次能坚持多久_(:3」∠) […]
ホロ's avatar

咱和()的 2020 年?

就不要 不合时宜的 问为啥隔了接近三个月才写了…… (?)

以及第四年的总结 在咱的 Matters 上。

为啥这一年只写了八篇文章?

啊这……咱写字的地方越来越分散了啊……

Google Search Console 的表现,是怎么样呢?

这一年……?

因为众所周知的原因(COVID-19 啦……),于是几乎都是呆在家里, 然后被批判一番不务正业…… 那就记一下这一年玩过的游戏好了。

  • DJMax Respect V ,除了正式发售时突然降价引起抢先体验玩家不满和全程联网外加笨蛋反作弊插件以外, 只是 DJMax 这个老字号就能让咱接受前面的大部分缺点了。
  • 女神异闻录5 皇家版,给女神异闻录系列出加强版是 ATLUS 老传统了。
  • 集合啦!动物森友会,成功登上咱 Switch 游戏时间排行第一。
  • Project DIVA MEGA39s,咱为什么要买它…… FTDX 加上 DLC 它不香么……
  • Fluffy Store 和 ATRI -My Dear Moments- ,两部被推荐来看后觉得不错的视觉小说。(所以 fault - SILENCE THE PEDANT 还是没有出)
  • Untitled Goose Game,其实 2019 年在 PS4 上玩过了,今年借着在 Steam 上发售又玩了一遍。 (然而并没有人一起玩更新的双人模式……)
  • 妄想破绽和原神……一言难尽*2。
  • Halo: The Master Chief Collection,用手柄坑完了除了 ODST 以外的正常难度剧情,嗯…… 至少咱的老笔记本还跑得动。
  • 女神异闻录5 乱战 魅影攻手,第一次录下了全程 然后给友人做素材工具人(?)
  • Cyberpunk 2077,除了分不清是 Bug 还是剧情演出以外还好啦。 这话到底是赞美还是批评……
  • Spice & Wolf VR2, “什麼?你說今天12/10還有2077? 2077能有赫蘿嗎?” 新的动画鉴赏模式虽然短了点……
  • Stellaris, P社玩家没有一个无辜的……

当然除了这些以外还有些一直在玩的老游戏,也就是那些音游什么的。PSN 给咱发年度报告的时候去年游玩时间最长的 游戏还是 FTDX ……


剩下的一时想不起来了,就先这样吧。

's avatar

新的桌面工作站:HP Z2 G5 Tower

我的上一代桌面工作站是台戴尔的 Inspiron 660,购于 2013 年初。经过数年来的多次升级,它拥有了三代 i7 处理器、16GB 内存和固态硬盘。

其实这台机器的性能直到今天依然不算逊色,基本上可以满足日常使用。但是,因为一些个人原因,我开始日常需要开启多个虚拟机,于是这台机器的 CPU 就开始有些吃不消了。

考虑到这台机器已经有 8 年历史,于是我便决定让它退居二线,同时将主力工作站进行升级换代。

经过反复的挑选,我最终选中了惠普的 Z2 G5 Tower。它在这些方面非常吸引我:

  • 免工具维护
  • 极高的可扩展性,有着合理的升级路线
  • 没有光污染,但是不失颜值的机箱箱体

在我购买这台机器时,我其实已经听说了 11 代酷睿系列处理器即将发布的消息,但是我这台机器预期服役 5 年以上,感觉差上一代应该问题不大。

以及我买的配置其实听起来就感觉有点尴尬:i7-10700 / 8GB DDR4 2666 x1 / 2TB 机械硬盘,不过我自己已经有一些配件了,可以直接换上去用。

最终,成功在国内商家以 6.8k 拿下。

安装

因为卖家发的是顺丰,所以我下单以后第三天就到了。毕竟在内蒙古这种地方,这个速度已经算是很快了。

机箱的颜值确实不错,不过感觉比想象中的要略小一点。

  • 正面 IO:
    • 2 个 USB 3.1 Gen1
    • 2 个 USB 3.1 Gen2
    • 耳麦接口
  • 背面 IO:
    • 2 个 USB 2.0
    • 2 个 USB 3.1 Gen1
    • 2 个 USB 3.1 Gen2
    • 1 个 RJ-45(I219-LM,千兆以太网)
    • 线路输入和线路输出
    • 2 个全尺寸 DisplayPort
    • 1 个 VGA (Flex IO modules)

打开机箱也非常简单,只要扳动背面右侧的黑色开关即可。

后盖上粘贴的贴纸介绍了主板各个部位的用途。

中间那个横着的玩意是……显卡风扇。应该是辅助散热的,不过也给我后面的显卡安装带来了一些麻烦(见后文)。

工作站的主板。这块主板的可扩充性的确非常丰富,主要亮点有:

  • 4 条 DDR4 内存插槽
  • 4 个 SATA 3 接口
  • 两个 M.2 M Key 插槽(只支持 2280 尺寸)和一个 M.2 E Key 插槽(我这台已经预装了 AX201)
  • 4 个 PCI-E 插槽

顺便主板是前后一体贯通的,而且是非标准螺丝孔位,所以是没法更换市售主板的。不过对我来说无所谓啦。

传说中的 Flex IO modules;我这台配的是 VGA 输出。

预装的 AX201 网卡,支持 Wi-Fi 6 和蓝牙 5.0。

主板的 PCI-E 插槽,配置如下:

  • 1 个 PCI-E x16
  • 2 个 PCI-E x4(x1 信号)
  • 1 个 PCI-E x16(x4 信号)

其它配件

我的附带了一个笔记本尺寸的 DVD-RW 驱动器,不过还预留了一个空的 5.25 寸扩展槽。

700W 的电源适配器,有着 80 Plus 铂金认证。预留了两条 6+2 的显卡供电。

虽然是非标准的电源,不过使用一些高端显卡应该是绰绰有余。

Edit: 有 Telegram 群友指出这个电源适配器应该是符合 ATX12VO 标准的。电源参数看起来确实如此,但是实际上孔位和尺寸跟正常的 ATX 电源完全对不上,而且电源接口也是非标准的。

三星 8G DDR4 2666 内存。

东芝 DT01ACA200 机械硬盘(2TB / CMR);拆下来以后被我塞进 NAS 里了。

硬盘托架;这台机器可以安装两块 3.5 寸硬盘。

其它附带的东西

包装中除了主机本体,还有小册子、品字电源线、键盘和鼠标。

附带的键盘和鼠标;朴实无华,但是手感还算说得过去。

安装我自己的配件

随后,我安装了我自己已有的一些配件:

  • 显卡:ZOTAC GTX 1650 Super (4GB GDDR6)
  • NVMe SSD:铠侠 RC10 500GB
  • 内存:英睿达 16GB DDR4 2666

但是安装显卡时,我发现我扣不上机箱的显卡散热器。仔细一看,发现了问题所在:我的显卡的供电口位置很尴尬,恰好和散热器冲突。

所以,我只好先把固定散热器(?)的那个黑色的东西拆下来了,然后才有了足够的空间。拆下来以后看起来问题不大,机箱的显卡散热器还能固定住的样子。

那么把整个散热器都拆掉呢?我试了,结果机器在 POST 就会报错,抱怨风扇出现了问题。

理论上来说,我可以装个假负载,来让主板认为风扇在正常工作;但是这台机器的风扇接口也是非标准的,就有点尴尬。

使用体验

安装完毕以后,顺利开机,没有任何问题。

得益于 24G 的内存、8 核 16 线程的处理器和 Hyper-V,整体的体验是滑溜溜的,开 3 个 Windows 虚拟机都压力不大。

2021 年 4 月更新:因为发现 500G 的存储空间有点捉襟见肘,再加上 Chia 挖矿潮即将兴起,所以提前把固态硬盘换成了建兴 T10 240GB + 铠侠 RC10 1TB 的组合。很爽。

总结

感觉这台机器买的很值,相比我 8 年前的旧电脑,是一次非常巨大的飞跃。开心!

顺便一提,我在购买这台机器时,有考虑上一块更好的显卡,但是现在显卡实在是贵的太离谱了,就先把之前买的 GTX 1650 Super 装上了。(摊手

ホロ's avatar

Arch Linux 上的 GLNMP

前言

请允许咱引用一下 lilydjwg 的一段话:

我之所以现在记录这事儿,「现在」的原因是,我要在另一个系统上再测试一遍再发布出来, 「记录」的原因是,下一次我就不用想要执行哪些命令了。

所以这篇文章更多是留给咱自己看的,绝对不可能适合所有人(例如万一有人是为了塞给 V2Ray 一类的代理软件用什么的)。

记得 Read the Fine Manual 是汝等的好伙伴就好啦。

那为啥要叫 GLNMP ?

如果汝会这么问的话,那应该是有听说过 LAMP/LNMP 系统吧,大概就是 Linux,Apache(或者 Nginx),MySQL(现在可能更多在用 MariaDB) 和 PHP(还有可能是 Python 甚至 Perl?)的组合。因为除了此上下文环境中的“Linux”实际上指的是 GNU/Linux 操作系统 …

RecursiveG's avatar

Ciphersuite Memo

I'm sorry if you landed in this keywords soup only to find it not helpful.

  • Key Exchange

    • DH (Diffie-Hellman): g^{xy} = {g^x}^y = {g^y}^x
    • ECDH (Elliptic-Curve DH)
    • ECDHE (ECDH Ephemeral)
    • DHE (DH Ephemeral)
    • RSA (Encryption): Generate a random bitstream and share it with the peer by encrypting using peer's RSA public key.

    A related concept is PFS (Perfect Forward Secrecy). DH offers PFS while RSA cannot.

  • Authentication
    Also known as key-signing. Commonly used together with the PKI(Public Key Infrastructure).

  • Encryption
    Used for data confidentiality.

    A related concept is mode of operation,which turns a block cipher to a stream cipher. CBC is a commonly used one. When it's used with AES, it's expressed as AES-CBC

  • Message authentication
    Used for data integrity. These algorithms are also called MAC(Message authentication code).

  • Authenticated Encryption (AE)
    Combines confidentiality and integrity. Wikipedia: Authenticated Encryption.

    • EtM (Encrypt-then-MAC): A secure way to combine encryption algorithms with MAC algorithms.
    • GCM (Galois/Counter Mode): A mode of operation, when paired with a block cipher, offers AE (actually AEAD) in one step.

    Some commonly used AE methods:

    • AES-CBC with an HMAC e.g.AES128-CBC-HMAC-SHA256.
    • ChaCha20 with Poly1305.
    • AES-GCM
  • Authenticated Encryption with Associated Data (AEAD)
    Similar to AE, but allows extra unencrypted data (associated data) to be authenticated. Roughly speaking:

    ciphertext = Encrypt(plaintext)auth_tag = Mac(associated_data + ciphertext)

    A common use case for AEAD is when encrypting a network packet, you want the packet header to stay unencrypted (for network routing purposes) but still authenticated.

  • Note about DH and curves of EC-based algorithms
    DH-based algorithms may have a “Group” option, which specifies a prime field or an elliptic curve. If a prime field is used, such as modp2048, it's normal DH. If an elliptic curve group is used, such as ecp256, it's EC-based DH.
    Some other curves may be used:

AstroProfundis's avatar

用 openSUSE Kubic 搭建一个 Kubernetes 集群

最近给家里几台闲置机器拆开重新整理了一下,然后统统装上了 Proxmox 做虚拟化,然后开出小鸡来想着搞点没玩 ...

>>Read more

OX's avatar

做了一個Amazon Echo Dot (3rd Gen.)的固定支架

根據自己的需要製作的模型,用來固定Amazon Echo Dot第三代在牆上或者任何平面上,甚至天花板。

沒有考慮電源適配器的擺放,單純只是固定Echo Dot,一般來説,使用這個支架需要一個穩定的釘子或者螺絲。

可以使用現有的,已經固定好的螺絲或者釘子,預留了足夠的穿孔大小,一般來説不太會出現螺絲頭太大穿不過的情況。

如果使用鐵釘等較細的釘子或者螺絲時,可使用上方兩側的預留孔。

 

 

直接使用自攻螺絲固定時,可直接在中間預留的凹槽處鑽入。

 

 

下載模型檔案(STL):echo dot 3_1.5-stl

模型檔案以MIT許可證授權使用和修改及再發佈。

 

這篇文章 做了一個Amazon Echo Dot (3rd Gen.)的固定支架 最早出現於 OXの胡說八道

's avatar

二之路

我很早以前就像写一点东西记录一下自己二次元的心路历程了。我的记性算是差人一等的,很多年前的事情也记得七零八落的了。也正是因为如此,我希望能在脑子变得一干二净之前能尽可能多的记录下来,所谓“salvage”大概是如此吧。因为记忆问题,如果有差错也大概死无对证了。

Phoenix Nemo's avatar

在 Linux 系统中升级超微 BIOS 固件

最近突然有客户找来说 BIOS 进不去了呀…看截图 stuck 在 POST Code AB 大概就知道什么情况了。

这不就是经典的 y2k bug 再现嘛…

一般情况下,升级超微 BIOS 固件的推荐方式是制作 DOS 启动盘引导系统升级,不过现在 BIOS 进不去也没有物理 access 就只好在 Linux 里操作啦。

获取设备信息

简单命令 lshw

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
~> lshw | head -n 25
localhost
description: System
product: X9SCL/X9SCM (To be filled by O.E.M.)
vendor: Supermicro
version: 0123456789
serial: 0123456789
width: 64 bits
capabilities: smbios-2.7 dmi-2.7 smp vsyscall32
configuration: boot=normal chassis=desktop family=To be filled by O.E.M. sku=To be filled by O.E.M. uuid=[redacted]
*-core
description: Motherboard
product: X9SCL/X9SCM
vendor: Supermicro
physical id: 0
version: 1.11A
serial: [redacted]
slot: To be filled by O.E.M.
*-firmware
description: BIOS
vendor: American Megatrends Inc.
physical id: 0
version: 1.1a
date: 09/28/2011
size: 64KiB
capacity: 8128KiB

X9 主板不是 2015 年就 EOL 了吗这可真是够老了…

总之主板型号是 X9SCM 于是在超微找到了新版的 BIOS 固件。下载到系统中解压得到一堆文件,其中 X9SCM1.106 这个文件就是需要的 BIOS 固件本体。

编译 SUM 内核模块

Supermicro Update Manager (SUM) 是用于在系统中控制 BIOS/BMC 的程序。首先下载并解压,得到 sum 二进制文件和一堆其他东西。在 driver 目录中发现了对应发行版预编译的内核模块,但是直接 insmod sum_bios.ko 出错,好在它也提供了源码,那么就直接编译吧。

首先安装对应内核的 Linux 头文件,搜索 linux-headers 一般都可以找到。

1
2
~> uname -a
Linux localhost 4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28) x86_64 GNU/Linux

然后

1
~> apt install linux-headers-4.19.0-13-amd64 build-essential # 还需要 make 和 gcc

进入 driver/Source/Linux 执行 make,如果成功编译,则 insmod ./sum_bios.ko

嗯,这次没有报错了。

升级 BIOS

升级命令和其他主板商以及 RH 系的包命名风格一样喜欢大小写混合 = =…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
~> ./sum -c UpdateBios --file ../X9SCM1.106/X9SCM1.106
Supermicro Update Manager (for UEFI BIOS) 2.5.1 (2020/11/12) (x86_64)
Copyright(C) 2013-2020 Super Micro Computer, Inc. All rights reserved.

WARNING: BIOS setting will be reset without option --preserve_setting
Reading BIOS flash ..................... (100%)
Writing BIOS flash ..................... (100%)
Verifying BIOS flash ................... (100%)
Checking ME Firmware ...
Putting ME data to BIOS ................ (100%)
Writing ME region in BIOS flash ...
- Update success for /FDT!!
- Updated Recovery Loader to OPRx
- Updated FPT, MFSB, FTPR and MFS
- ME Entire Image done
WARNING:Must power cycle or restart the system for the changes to take effect!

至此就基本完成啦。然后直接重启即可生效。

不过还是需要注意一下,升级固件都是有变砖风险的,某些情况下需要先联系硬件厂商支持确定升级路线。

以及新版 X10 开始升级 BIOS 需要激活 license 了… 嘛。听说超微的密钥早就被提取出来写了算号器了(什

最后的最后,提醒各位到 2038 年只有 17 年了哦(笑

17windy's avatar

TimeSpan参数表格(转记)

偶然用到TimeSpan,这里记录一下format…
RecursiveG's avatar

IPsec 配置备忘 Part9 - RouterOS

这篇来填一个在 Part1 的时候挖的坑,简单介绍一下怎么在 RouterOS 和 strongSwan 之间配置一个 Site-to-Site 的 IKEv2 VPN。如果你还没有动手实际配置过 strongSwan,我强烈建议你先读完至少 Part1~5 和 Part7,并且自己动手配置一个能用的 strongSwan 服务端。这样至少能保证你会配置 strongSwan,否则同时学习 RouterOS 和 strongSwan 两种配置方法会让人云里雾里。

网络结构

RouterOS 做路由器,用 DHCP 给内网设备分配192.168.50.0/24段中的 IP 地址。并且用 IPsec 的 Tunnel 模式将所有来自此 IP 段的数据转发至服务器22.22.22.22。路由器本身的 IP 并不重要。

阅读基本 RouterOS 命令

在本文中我主要使用 RouterOS 命令来表示具体的配置,但是实际情况下调试 WebUI 会更方便。这里提供一张简图解释怎么阅读 RouterOS 的命令:

RecursiveG's avatar

Flexget+systemd.timer 配置

之前一直都是手动检查每周新番并添加到 Transmission 中的,最近尝试把这一流程自动化一下。简单搜索了一下发现了 FlexGet 这个工具。支持 Transmission 也能在 Linux 下运行,就决定是它了。

安装到用户目录

ArchLinux 源里并没有这个包,又考虑到这是个 Python 程序,所以就决定直接用venv了:

1
2
3
4
5
6
mkdir ~/.config/flexget
cd ~/.config/flexget
python -m venv virtualenv
source virtualenv/bin/activate
pip install flexget
pip install transmission-rpc

配置文件

配置方法可以看教程也可以看官方文档。我就只简单贴一下。

~/.config/flexget/config.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
templates:
transmission:
transmission:
host: 'https://domain-name.com/transmission/rpc'
port: 443
username: 'your-username'
password: 'your-password'

tasks:
这里随便写:
rss: 'https://mikanani.me/RSS/...'
accept_all: yes
template: transmission
transmission:
path: '/folder/path/on/server'

Systemd 配置

~/.config/systemd/user/flexget.service
1
2
3
4
5
6
[Unit]
Description=Refresh RSS with flexget

[Service]
Type=oneshot
ExecStart=/home/recursiveg/.config/flexget/virtualenv/bin/flexget execute

用户登录后 5 分钟进行同步,然后每两小时检查一次。

~/.config/systemd/user/flexget.timer
1
2
3
4
5
6
7
8
9
[Unit]
Description=Run flexget on login then every 2hrs

[Timer]
OnStartupSec=5min
OnUnitActiveSec=2h

[Install]
WantedBy=timers.target

一些可能会用到的命令:

1
2
3
4
5
6
7
8
# 启用并运行定时器
systemctl --user enable --now flexget.timer
# 立即检查
systemctl --user start flexget.service
# 检查定时器情况
systemctl --user list-timers
# 检查日志
journalctl --user-unit flexget.service -f
AlisterTT's avatar

2020年最后一天搞崩了VPS,2021年第一天修好了它。

这个VPS是2017年部署的了,当时随便选了操作系统ubuntu 16.10,一直没管升级,结果今年一看,源已经都不能用了,全世界都找不到源,就瞎鼓捣,一个autoremove把php给删了,这下瞎了,我甚至没有提前备份WordPress的数据。

用dump备份了数据库,www文件夹全部拷贝到本地,服务器重装系统,装了UBUNTU 20.04 LTS,NGINX PHP还有V2RAY倒还好配置,MySQL卡了半天。因为UBUNTU 20带的MySQL是8.0了,相比之前5.7变化比较大,主要是用户权限这一块,重来了好几次,最后干脆装回了5.7。

实际操作顺序大概就是,先安装配置好V2RAY,配置BBR,然后apt无脑安装NGINX和php,配置NGINX文件,安装phpmyadmin,安装配置mysql,添加WordPress数据库,安装WordPress,然后把dump备份的sql文件导入覆盖wordpress数据库,把之前的wp-content文件夹里的内容恢复,设置html目录权限777,齐活,差不多就这样吧,懒得打字了。

's avatar

我的 2020

好耶,我终于把 2020 年勉强过完了。

成就

我在 2019 年终总结里写道:

待定!计划赶不上变化的。

结果,受到 COVID-19 疫情的影响,今年的大计划其实几乎都泡汤了(包括计划参加的一个比赛、2020 年夏天的面基出游计划等)。

所以,总体上感觉今年是碌碌无为的一年。不过没办法……

买买买

不过今年买到了不少很棒的新硬件:

  • WI-1000XM2
    听感很好,佩戴起来相当舒适,而且 10 个小时的续航还是很香的。
  • 自组装台式机
    我第一次亲自从头开始组装的台式机,虽然有些配件选择上的遗憾,不过点亮后还是很有成就感。
  • 一加 8T
    强大的骁龙 865、65W 快充(虽然是私有协议)、滑溜溜的 120Hz 屏幕、很强的可折腾度……用起来太爽了!
  • AirPods Pro
    出门使用还是很方便的,包括超级简单的多设备切换功能!

今年并没有购买很多专辑,不过还是感觉这些是很不错的:

博客

今年的确没有写很多博客,不过还是做了不少工作,让它「符合潮流」:

  • 将评论系统后端用 TypeScript 进行了重构,修正了很多错误
  • 给自己博客增加了暗色模式,并支持跟随系统切换
  • 给自己写了一套全新的卡片式博客主题,使用了新的设计语言

2021 年计划?

's avatar

旧闻:我组装了一台新电脑

(故事发生在 2020 年 5 月;这篇博文写了一半就咕了,直到现在才发出来)

我校即将开学,而由于我们需要用到的一些软件是 Windows 限定的而且非常吃资源,加上由于 COVID-19 疫情我们要在校园内关上一个多月,所以决定组装一台新的 Windows 台式机搬过去用。

这样,我还可以未来让家里那台古董的 i7-3770 退居二线。

配置选择

由于手头的钱不是特别多,所以总体来说只是达到了流畅能用的水平,后期还有很大的升级空间。
这也是我第一次完整的组装一台(同时还搭配了中端独显的)台式机。

部件 名称
CPU Intel Core i3-9100F
GPU ZOTAC GTX 1650 Super (4GB GDDR6)
主板 技嘉 B365M GAMING HD
内存 英睿达 DDR4 16GB 2400(单条)
SSD 铠侠 EXCERIA NVMe 500GB
风扇 Intel 盒装风扇
电源 CoolerMaster MWE450(450W 铜牌)
机箱 CoolerMaster 特警365

不足之处

  • CPU 选择了不带核显的 i3-9100F,导致了它必须搭配亮机卡使用,使得它日后用于组建家用服务器的价值大减(除非买板载显卡的服务器主板,但是它们一般都超级贵)。
  • 主板选择了 M-ATX 尺寸的主板,因为我手上正好有两个(白嫖的)联想的空箱。但是装上以后,才发现这俩空箱同时存在一些问题:
    • 散热很拉跨。
    • 接线都是非标准的,需要拿杜邦线转接。
    • 内部构造极为蛋疼,不利于维护。

挑来选去,我买了一个可以塞 ATX 尺寸主板的 CoolerMaster 特警365。这款机箱具有以下优点:

  • 没有光污染和 RGB
  • 朴实无华但外观说得过去
  • 空间宽敞
  • 散热良好
  • 自带一个 12cm 的风扇

所以,我的主板选择其实也是很失误的。B365M GAMING HD 这块主板主要的特色就是小巧苗条,但它并没有提供 M.2 WiFi 接口,所以我安装无线网卡的方案也很受限;假如我选择 ATX 尺寸的主板则完全可以避免此问题,同时获得更多的扩展空间。

搬运

开学报到那天,我把我新装的台式机和显示器什么的都装进我的拉杆箱里。然后,我的拉杆箱重量达到了 25kg。我就那么把我极度沉重的行李从我家搬运到我校开启的唯一一个大门,又搬运到了宿舍楼。

假期到来时,我又把这个极度痛苦的过程重复了一遍。嗯,没有小车、没有电梯、宿舍楼距离唯一开放的校门超级远,体验可想而知了。

结论

这其实不算是一次非常成功的台式机组装选择,但它还是让我平安完成了我们的课程任务。所以,接下来我还是考虑搭建远程 RDP 工作站了(发稿前更新:已实施),这样可以只带上笔记本去学校,同时享受到 Windows 台式机的优势。

SgDylan's avatar

同 2020 道别、相约 2021

再次,同样很普通地,为 2020 划上句号吧。


imi415's avatar

STM32H7-QUADSPI XIP及优化(WIP)

H750的128K真的不够用
RecursiveG's avatar

在 GNS3 中使用 ArchLinux

之前在折腾 strongSwan 配置的时候需要多台电脑互相连接做测试,用实机各种不方便。比方说,我不想暴露真实 IP,就只能在发布前手工编辑配置文件,也不知道编辑过后的文件到底能不能工作。于是尝试折腾使用 GNS3 来搭建一个虚拟的网络环境。

安装 GNS3

你可以尝试从 AUR 安装 GNS3,但是我之前试了几次都不是太成功,于是还是把 GNS3 装进了 virtualenv 里:

1
2
3
4
5
6
7
8
9
# 创建并进入 virtualenv
virtualenv -p python3.9 virtualenv
source virtualenv/bin/activate
# 安装 GNS3 及 PyQt5
pip install gns3-server gns3-gui pyqt5
# 安装一些其他的依赖(可能有漏的,请根据 gns3 的出错消息自己安装)
yay -S vpcs dynamips
# 启动 GNS3
gns3

GNS3 支持好几种方式来运行虚拟网络中的“节点”,我这里使用 Docker 来运行虚拟的 Archlinux 系统。因此也需要先安装:

1
2
3
sudo pacman -S docker
sudo usermod -aG docker `whoami`
sudo systemctl start docker

同时建议安装 Wireshark 方便抓包调试。

连接因特网

进入 GNS3 创建 Project 以后就可以尝试把左侧列表里的设备往中间画布上拖了。但是你会发现不仅设备类型少的可怜而且也基本拖不上去。这时我们需要自己创建设备节点的模板。进 Preferences,在最下面的 Docker containers 里点 New,Image name 输archlinux:base-devel,其他的都默认,Adapters 可以按自己需要指定数量。

点 OK 后,左侧的设备列表里就多了一个设备。拖到画布上,右键 Start,再右键 Console 就可以看到 ArchLinux 的命令行界面了。

GNS3 默认使用 xterm 作为终端模拟器,如果你像我一样使用 Gnome Terminal,需要先去 General - Console applications 把启动终端的命令改成

gnome-terminal -t "%d" -- telnet "%h" "%p"

这个新创建的 Archlinux 节点还是空空如也的,除了基本的系统什么也没有,我们可以把它连接到实际的网络上,这样就能用 pacman 安装软件包了。先从设备列表里拖一个“Cloud”设备出来,然后点左侧边栏最下面的“Add a link”切换到连线模式,点 Cloud 节点,选择物理机上用于联网的接口,再点 Archlinux 节点,选择要连接的端口,这样一条连线就接好了。Archlinux 节点被直接桥接到了外部接口上,和宿主机位于同一网段。

由于 Archlinux 的镜像不带 dhcpcd 无法自动获取 IP,只能手动设置一下咯:

ip link set eth0 upip addr add 192.168.1.222/24 dev eth0ip route add default via 192.168.1.1 dev eth0echo nameserver 1.1.1.1 > /etc/resolv.conf

不过实际测试以后发现这个速度实在是很残念,可能还是要写 Dockerfile 把镜像配置好再用才行。

依云's avatar

一次失败的 KDE 尝试

本文来自依云's Blog,转载请注明。

前些天尝试了一下 KDE 桌面环境,不过实在是没能用下去。

首先要说的是,KDE 桌面确实漂亮,非常养眼。设置项也挺多,可定制性还是挺不错的。只可惜问题同样很多。

首先是显示器缩放的问题。受限于 X11,KDE 只能设置一下全局的缩放比例。所以我们只好缩放显示器显示的画面。不幸的是,一向相当体贴的 KDE 此时却笨笨的,在使用 xrandr 设置好之后需要重启 plasmashell 来使其获取 xrandr 的设置更新

kquitapp5 plasmashell && kstart5 plasmashell

KDE 的设置项很多,分门别类地在「设置」应用程序中集中列出来,然而问题也由此产生:同时只会显示一个「设置」窗口。也就是说,我配置快捷键时,想去窗口管理器那边看一看,调整一点选项,就必须放弃我当前打开的快捷键视图,放弃我键入的搜索词,并且选择「应用」或者「放弃」更改,才能切换到另一个「设置」组件中去。即使从 krunner 里打开某个组件的设置,它也会找到并更新已有的窗口。

我知道 Windows 10 也是这么个「单任务」设置的风格。可 Windows 10 也没有这么多可以设置的地方呀。后来获知有个命令可以打开单独组件的设置窗口。很不方便。它被隐藏起来的原因是这种窗口不能返回到组件列表界面,会让用户困惑。可是,为什么我不能同时打开「设置」的不同组件的多个窗口?单独组件的窗口会让用户困惑,那就不要用单独组件的窗口就好了嘛。

KDE 桌面还有个问题:启动特别慢。登录进入界面要好久,启动一个程序,它的图标也要跳好久窗口才会出现。不知道它在干什么。我甚至怀疑它是为了展示启动动画而故意推迟界面的显示。

KDE 有提供丰富的桌面部件。我往副显示器上放了一些系统状态的监视器——CPU、磁盘、网络啥的。然后问题来了:我凑齐了四个部件刚好形成2x2的网格,可是我要怎么对齐它们呢?并没有对齐的选项,也没有吸附的功能。在我找到它使用的配置文件并手动修改之前,我只能用肉眼瞅。可计算机不就是用来做这种人不擅长而机器擅长的事情的吗?

终端我还是用 GNOME Terminal,因为有些特性(比如超链接)只有它支持。但又出现问题了:它启动之后,pin 它的任务图标,或者通过任务栏图标创建新实例均会失败。把它 pin 到任务栏上,需要从主菜单的右键菜单里操作。即使这样,启动之后终端窗口还是会位于新的图标,旧图标还是不对应任何窗口。后来查了一下,GNOME 的东西都没有主动支持启动通知,导致 KDE 很多时候只能猜测,而这次它猜错了。解决的办法是给 GNOME Terminal 的 .desktop 文件加上正确的 StartupWMClass 项。这其实不是 KDE 的问题,但也没办法。KDE 不想为别人擦屁股,GNOME 不在意自己的软件在别的桌面上的可用性。

不过 Qt 写的 flameshot 我就不知道是怎么回事了。具体情况不记得了,反正就是显示异常。好像是全黑吧。我没来得及 debug 这个。

最后,让我决定放弃 KDE 的点来了:我设置不了我需要的窗口管理快捷键

切换窗口,默认是 Alt-tab 的那个,我好不容易在「快捷键」设置里找到了添加更多快捷键的方式,但我发现除了 Alt-tab,我自定义的都不能连续切换窗口。按一下,切换一下,然后就切不动了,只能放开快捷键。后来了解到这是设置更新方面的问题,kwin_x11 --replace一下就有效了。

切窗口其实问题不大。问题大的是切显示器屏幕。两个功能:一、把焦点切到另一个屏幕;二、把当前窗口移到另一个屏幕上。

前者可以勾选「分隔屏幕焦点」选项,然后调整一下「阻止盗取焦点」的级别。我也不知道这个级别都是啥意思。「无」我能理解,「低」「中」「高」「终极」都是些啥?反正调整一下,确实可以把窗口焦点切换到另一个屏幕去了,除了鼠标不会跟着过去!另外测试过程中,有时候焦点会丢失——我不知道当前什么窗口获得了焦点,也不知道接下来谁会获得焦点。比 Mac OS X 里焦点跑到一个窗口也没有的 Finder 上还要神秘。

不过这个倒是可以自己写个脚本解决:使用 xrandr 获取屏幕的大小和位置,通过 X 的接口获取鼠标的位置并通过 Xtest 扩展来移动它,然后再用某个 X 的接口去设置窗口焦点——完全绕过 KDE 的功能。

然后我被另一个问题难住了——我怎么把窗口移到另一个屏幕上并且把焦点也移过去呢?使用文档匮乏的 kwin script 是可以把窗口移过去,然后我没能找到移动鼠标光标的 API。通过 X 是可以移窗口的同时移鼠标,但是我拿不到带窗口装饰的窗口位置信息。kwin 有一个 getWindowInfo 的 D-Bus 接口,但是它接收的那个 UUID 参数,我没找到获取的方法。

总结一下,KDE 对快捷键的支持并没有想像的那么好,尤其是多显示器的支持。快捷键的设置是通过图形界面来操作的,虽然直观但是对于大量快捷键的管理来说非常困难。而对于大显示器来说,通过快捷键来管理窗口是十分必要的——因为我更难肉眼找到我的鼠标光标去了哪里。

接下来,我打算一边忍受着 Awesome 3.5.9 的旧与 bug,一边尝试将 i3 改造成我需要的样子。

violet's avatar

写在一年之际(二)

上篇写了猫,这篇写我吧。我其实非常不想谈这段经历,想起来就觉得挺难受的。

去年因为这样那样的原因以及一些骚操作加自尊心,本来有个亚麻 offer 也浪费了,当时的想法是老子来了之后再找,我要是知道后来发生啥非得抱着人大腿把 offer 求回来。

远程工作

这里要非常感谢我在前东家的老板,帮我办了几个月的远程办公,一定程度缓解了我找工作的压力。虽然远程工作没有很重的业务需求,不过也非常锻炼人的沟通能力,我以前觉得沟通对于我来说小菜,后来发现如果涉及到语言障碍和思维方式其实还是门深的学问。当然不要来跟我杠我的英语能力,我在口语上没有啥问题,虽然跟 native speaker 拼不过,但是不算差的那派。主要还是跟人沟通的思维方式上,如何进行有效的沟通也是一门学问。

刷题

老实说我不会刷题,上大学的时候因为 C++ 的作业是 ACM 题,我真是哭了又哭,反正我也不是啥好学生,题是真刷不动。但是为了找工作不得不开始刷,Leetcode 买了会员,分门别类开始学习。我其实更想说明区分刷题和学习,刷题只是过了一遍题,学习是学习怎样写这样的题,怎样的方式可以优化,遇到题想到考点在哪里。随着深入学习还要脱离编辑器的辅助,努力追求正确率,都是需要练习的事。人老说做题家刷题家,我无所谓,反正是面试敲门砖而已,我做好自己就行,自己躺地上嫌别人学习那我没得办法。

面试

我一直怀疑2020是我的本命年,甚至我追问了很久我妈是不是生错时间了。由于众所周知的原因,3月开始 lockdown,当时又面了一次亚麻,但是因为 virtual 面试写题的时候有点慌乱,没有 bug free 所以就挂了。之后很长一段时间网上也鲜有新职位,我就老实刷了一段时间题。6月开始不断投简历面试,怎么说……就是觉得差点缘分,有面着面着没信的,有面试完全没问题我还教面试官怎么写回头就跟我说实在抱歉,有 OA 做完明明 90% 以上但还是跟我 unfortunately 的,有等着安排面试时间回头跟我说他们找到人了,有以我没有 PR 拒的,更不要说简历投出去石沉大海的。期间也有几个猎头在紧密联系,不过都是些 start up,一个脾气不对就挂了(我还没说他们给我乱安排面试时间还放我鸽子),总之就是一刷邮箱眼泪就掉下来。我从来没有经历过如此低谷的时刻,每天都在自我反省到底我哪里做错了觉得我也没有这么差劲吧,脾气暴躁跟大柱一言不合就摔电脑,一刷 SNS 看大家的工作生活如此丰富多彩就开始难过。还有些人落井下石给我添堵我谢谢你啊。

很长一段时间都不想上网,啥都不想干,完全靠着大柱和几个朋友的支持坚持过来。大柱在我哭我撒泼我沮丧我难过的时候乖乖地抱着我。悦姐看我不说话不理人找我视频聊天,wandou 也在鼓励我,大毛还跟我一起动森聊了会天还给我寄了口罩,现在想起来都是我想流着眼泪感谢的人。

好在投了好久简历之后终于接到现在这家的 offer,在面试的时候就让人觉得很舒服,面试官(我后来知道是个士大夫)水平真是高,好多东西我觉得没问题,经过他一点播才发现噢!有道理,而且还有几个面试官聊过之后觉得真不错,有了 offer 就马马接了。事实证明这家真的不错,虽然公司小,但是同事水平是真高,我以前不觉得自己拉胯,现在发现最拉胯的是我(我要赶上!)

爱好

众所周知我是个指甲油爱好者,今年最大的变化是开始拍视频。一开始只是心情难过找点事干,一开始买了个手机支架开始涂开始拍,一年过了之后涂/印花/拍摄/剪辑的水平突飞猛进,甚至买了非常棒的相机和镜头搞得更专业一点。好多人都说开美甲店啥的,但那不是我的兴趣:),我还是好好拍视频玩玩就好。

没啥好展望的新的一年,世界回到正轨就好,找回失去的一年。

violet's avatar

写在一年之际(一)

一直在想写一个今年的总结,一拖再拖到了现在,今天早上被 Google 相册提醒,原来到加拿大已经一年整了。

我不是一个特别喜欢说情绪说经历的人,说多了都是矫情,甚至去年出国的时候也是跟交好的人吃了几顿饭然后就摸了出来,不过还是有一些事情值得记录一下。

拖家带口

自从开始准备来加拿大之后就在搜索如何带猫出国,我的功课如下。

适用地区:

北京

相关机构:

北京观赏动物医院、通州海关

加拿大海关要求:

超过一个月的狂犬疫苗证明

提前准备:

1. 超过一岁没有生病的猫(不要带小猫上飞机)

2. 符合航班要求的猫包(我带进机舱,加航的要求是55x40x27cm,比较奇葩的一个猫包尺寸,你买买就知道了,尽量符合高度限制就行)

3. 尿垫、便携猫砂盆和小包猫粮

办理过程:

1. 在出发前至少一个月(我提前了三个月)去北京观赏动物医院去打狂犬疫苗,说清楚是为了办出境,他们会建议一并注射芯片,加好医院联系出境的联系方式。

2. 确定人的行程后立刻打电话给航空公司买猫的票,能带进机舱的收费标准是100加币,每趟航班的宠物数量有限,一定要赶快打电话预定。另外猫算一件随身行李,所以理论上会少一个包,但很少有人查随身行李有多少件所以要求也不是特别严格。

3. 买好机票后立刻联系医院方,说明出发时间和出发地点,对方会建议一个时间去医院做体检和准备申报材料。

4. 带上护照、疫苗本和猫去医院体检,医院会帮忙整理材料申报给海关,医生建议2天后去海关处拿申报材料,但也说一般第二天就可以去拿。

5. 体检后第二天带猫和护照疫苗本去通州海关缴费、给猫查体温做基本检测以及拿到通关文书。

6. 出发哄骗吃好拉好(但明显不会听话不会拉撒)。带猫去机场 checkin,说明带猫。过海关的时候拿通关文书敲章。

7. 落地走申报通道,还是拿那堆通关文书,交进口税(21刀?30刀?)

中间趣闻:

狂犬疫苗:北美这些国家是消灭了狂犬病的国家,所以带宠物入境是需要注射狂犬疫苗的。狂犬疫苗非常容易致敏,麻达在注射了狂犬疫苗后开始出现了摇头晃脑,爪子特别烫的症状。我找护士看了看,对方说没事。但是在我带猫回去的路上开始呕吐而且出现了应激反应,赶快回医院,这个时候小猫咪已经出现了水肿,整个脑袋肿了起来,鼻梁老高。打了抗过敏药之后观察了一个小时才回去。如果大家要打狂犬疫苗一定要多观察一会,有任何异常一定要找医生看。另外防杠声明:猫三联里不包含狂犬疫苗,我们注射过猫三联。如果猫不出门的话狂犬疫苗一般不是必须的,因为根本没有感染源。

注射芯片:加拿大是不要求注射芯片的,但是我在打疫苗的时候医院强烈建议注射。我后来才知道去海关办通关文书的时候是要扫描芯片确认是疫苗本上的这只猫。

通州海关:我印象里政府工作人员都是冷冰冰而且给办事就算不错了,这次遇到了一个特别和蔼的阿姨,非常亲切,眼睛一笑眯一条缝。还祝我们一路顺利。

机场安检:安检的几个工作人员没有遇到过带猫过安检的情况。让我抱着猫过门,猫包单独过X光机。小哥很犹豫地问我它咬人吗?我说不咬随便抱!小哥还是很害怕,我就举着小猫咪让他摸了摸猫头猫腿猫肚子。

起飞和落地:虽然直飞只需要11个小时,但是加上提前出发和落地后到住处差不多要22个小时了。出发前各种哄骗小猫咪上厕所也没成功,而且怕中间饿我们还偷偷带了点冻干。但小猫咪精神明显不好,什么都不吃,刚开始的时候还呼呼大睡,后半段不耐烦开始小声叫。我带去洗手间放出来活动了活动终于好了点。入关的时候 CSBA 说你的猫呢,我举猫包起来给她看,小猫咪惊恐地瞪大了眼睛,她直呼 cute!我其实有点心酸,我是再也不想带猫再折腾一次了,人都觉得11个小时难熬,对于一只小猫咪来说真的是难上加难。

SgDylan's avatar

简单鉴别常见有损音源编码器

最近写 FLAD 的时候发现,可以用肉眼简单辨别立体声音频使用的有损编码器,在此简单记录。

依云's avatar

i3 的 scratchpad 处理逻辑

本文来自依云's Blog,转载请注明。

i3 有个东西叫「scratchpad」,和我在 Awesome 里用的 run_or_raise 功能有些类似。

我的需求是某些浮动窗口可以「招之即来,挥之即去」。上次尝试切换 i3 遇到的一大麻烦就是,我经常从终端里启动图形界面的程序,而启动完之后我得手动给我的终端找个地方放着。i3 不支持最小化,也只有十个带数字快捷键、可以快速访问的工作区,所以 scratchpad 很重要,但是它的行为我有些捉摸不定。

首先是 move scratchpad 这个命令。它会把当前 con(窗口或者容器)浮动、取消全屏,然后移到一个叫 __i3_scratch 的不显示的工作区。

然后是 scratchpad show 命令(动词放后边了)。如果没有指定条件,它有如下复杂的处理逻辑:

  • 检查当前窗口是不是去过 scratchpad。如果是,就把它丢回去。
  • 否则检查当前工作区是否有另外的 scratchpad 窗口。如果有,就给它焦点。
  • 否则检查其它工作区是否有另外的 scratchpad 窗口。如果有,就把它移过来。
  • 否则把 __i3_scratch 里最久没有「见到光」的窗口移过来。

如果指定了条件,那么这样检查匹配的窗口:

  • 如果窗口不曾去过 scratchpad,什么也不做。
  • 否则如果窗口去过 scratchpad 并且在当前工作区,就隐藏它。
  • 否则就把它移过去。

总结一下,就是「回去,或者回来」。虽然动作的名字叫「show」,但其实是一个类似于 toggle 的功能。它的麻烦之处在于:如果你有多个去过 scratchpad 的窗口,你很难控制出现的是哪个窗口。一个绕过这个问题的办法是,总是带条件地使用 scratchpad。另一个小麻烦是:没有办法在匹配的窗口已经显示的时候,不要把它隐藏掉——有时候我只是习惯性地呼叫我的终端,而不看它是不是已经在我面前了。

对于浮动窗口,i3 有很多奇怪的限制,或者说是未实现:

  • 不支持最小化
  • 浮动窗口也不能显示在平铺窗口之下(加上上一条,就是没办法暂时藏起来)
  • 不支持最大化(手动调整窗口大小无法自动适配显示器大小,也没有「恢复」一说)
  • 不支持显示在最上层(当你在 GIMP 里开了一堆图片需要局部对比时)
  • 有全屏窗口时不能显示浮动窗口(看视频无法临时使用浮动窗口查个单词啥的)
  • 切换窗口时,平铺窗口和浮动窗口是隔绝的(需要单独的快捷键来切换)
17windy's avatar

尝试了一下用Unity开发小型行业软件

如题,之前稍微学了下python写过一个超简单的,被吐槽太丑了,于是还是先用Unity来搞一个,出乎意料的执行效率没啥问题。

OX's avatar

日本國內送金でチャールズ・シュワブ國際投資口座に入金する方法

チャールズ・シュワブ(Charles Schwab)の公式入金ガイドでは、シティバンク(Citi Bank)の支店にあるシュワブの受取口座に資金を送金するために、高い手數料がかかられる海外送金って方法のみになっている。

実際、現地の銀行口座(日本など)を持っていれば、現地の電信送金を使ってシュワブの口座に入金することができる。

まず、シュワブは米ドルだけでなく、日本円建て送金も受けられる、ドル以外の入金する場合はシティバンクがオープン為替レートで米ドルに両替し、ドル建てで投資口座に入金する。

國內送金で入金する時、最初に迎える問題といえば、シュワブ日本の入金口座は10桁であること。(日本の銀行システム的には口座番號の上限が7桁となっている)

電話でシティバンクに問い合わせするところ、シティバンクは、國內の地方銀行が國內振込で受取人の口座番號を正しく記入できないことを知っており、これを迴避方法をおしえてもっらた。

それは振込用紙にで10桁の受け取り口座番號を前の7桁切り取って受け取り人番號に書いて、殘りの三桁は受取人名義の前に書くこと。

ここでは一例をお參考に

受取人口座番號:0159051

受取人名義:404 Charles Schwab & Co.,  Inc.

ですが、受取人口座番號欄に存在しない番號になってるため、銀行のシステムがになるのは當然のことである、そのため、ネット銀行サービスは使えずに、銀行の窓口に行き、振込用紙を使って振り込むというやり方になる。

まだ、窓口で手続きしでも、當然エラーが出る、その時には銀行の人をそのまま実行しなさいと説得する努力が必要となる。

窓口で送金手続きが完了するとすぐに日本のシティバンク側に引き継がれ、當番のスタッフが受取人の名前の前に入力された3桁の番號を元に、正しい10桁の口座番號を手動で修正して送金を成功させる。

まだ、海外送金の場合、送金伝票にメッセージ欄があるが、國內送金の場合はそのような欄がない、これを解決するにはシュワブ投資口座の名義と番號を振込依頼人名に詰めて書けばよい。しかしそれをそのままに書いてだけではなく、前に「FORTHER CREDIT TO」を入れどいてのは重要のポイント。

「FOR FURTHER CREDIT TO YAMADA YUKI 2740-1234」

まだ、振込依頼人名の文字數は上限があり、だいたいの銀行は48文字までになってるが、これは事前に取引金融機関に確認すると無難だろう。

振込依頼人名は何文字まで入力できますか? – 三井住友銀行

もちろん、名前自體が48文字以上であって方もおるだろう(これはレアと思うが)、それならば、シュワブに(短い)別名を追加したいって書類を提出することによって、この問題を迴避するできると思う。具體的にはシュワブさんに聞くこと。

もう一つ気をつけなければならないのは、日本で送金用紙に記入する際に「フリガナ」欄には、全てアルファベットの大文字で記入すること。

まとめ

  1. 銀行の窓口に振込用紙を使え
  2. 送金伝票の受取人口座番號欄に、10桁の口座番號の最初の7桁を書き、殘りの3桁は受取人の名前の前に書くこと。
  3. 振込用紙には、英數字、符號だけしか書くこと、カタカナ書けずに(これ重要)。

もしうまくいけば、本來6,000円程度の費用をかけなければならないことをただ800円程度で済ませる。

しかも國內送金なので、対応する送金は30分以內に日本のシュワブのシティバンク口座に送金できるので、海外送金の2営業日や7営業日よりもはるかに早い。

最後までお読みいただきありがとうございました。

這篇文章 日本國內送金でチャールズ・シュワブ國際投資口座に入金する方法 最早出現於 OXの胡說八道

OX's avatar

使用日本國內匯款方式入金Charles Schwab International投資賬户

前略。

Schwab給出的官方入金指引要求使用手續費高昂的海外送金(International Wire)方式給Schwab在花旗銀行(Citi Bank)的各地分行開設的收款賬户轉賬入款。

實際上,如果有當地的銀行賬户(比如日本的)是可以通過一種曲綫救國的方法,使用本土轉賬的方式給Schwab賬户入金的,接下來我以日本的實際情況給出具體的操作方法。

首先是Schwab除了接受USD匯款,其實還接受多種貨幣匯款,然後由Citi Bank按照當天公開匯率兑換成美元入金到投資賬户上,具體的收款賬户在Schwab的官網公開文件上有記載,你可以在這裏看到。

Schwab要求匯款時提供對應的收款賬户的户名信息,包括開户人的名字(Alias Name也是可以被接受的)和八位數的賬户號碼(Account Number)。這裏有一個隱性的必要信息就是賬户的持有人的身份證件住址,但是實際操作上這個並不是必要的,雖然如果是通過國際匯款(a.k.a 海外送金、International Wire)理所當然地就需要填寫送款人的住址信息,但是其實這不是必要的。

另一個問題是Schwab提供的日本花旗銀行的收款賬號有十位數,而日本銀行系統最長可以錄入的賬號長度是七位數,看似技術上就不可行,但是其實有方法可以補救。

日本花旗銀行知道日本本土銀行通過國內轉賬的方式無法正確填寫收款人的賬號,所以提供了一種補救的方法,那就是匯款時在收款賬户號碼一欄上填寫賬號的前七位數,剩下的三位數寫在收款賬户名的前面。

這是一個例子:

受取人口座番號:0159051

受取人名義:404 Charles Schwab & Co., Inc.

通過這種方式匯出的匯款請求會由花旗銀行負責入賬的操作員手動修正為正確的十位數賬户,使匯款可以正常入賬,這是我親自打電話給花旗銀行日本分行的工作人員詢問,對方給出的方法。

而日本幾乎所有銀行的網上銀行系統都會在執行匯款之前檢查對應收款賬户和收款人名是否匹配,如果不匹配則無法執行,所以為了繞過這個問題,你需要去銀行櫃枱匯款,使用紙質的匯款單。

而為了讓這種操作可行,還需要有匯出銀行方面的合作才行,具體來説,因為收款賬户實際上是十位數,只寫前七位數的時候系統理所當然的會報錯,因為目標賬户實際上並不真實存在,你需要説服負責匯款的銀行工作人員無視系統的報錯,實行這筆匯款業務。

只要這筆匯款被提交,這筆匯款的信息就會被日本的花旗銀行系統接管,再由當班的工作人員根據收款人名義前填寫的3位數字手動補足成10位數的正確的賬户號碼使這筆錢成功入賬。

然後是匯款附言的問題,海外送金時,匯款單上有提供附言的欄位,但是日本國內匯款並沒有提供這個欄位,那麼怎樣才能把Schwab賬户名和賬户號碼寫進匯款單裏面呢?其實很簡單,那就是寫在匯款名義人(振込依頼人名)一欄上。

比如JOHN SMITH 2740-1234,但是需要特別注意,光這麼寫是不可以成功入金的,你需要寫得很清楚,要在前面寫上“FOR FURTHER CREDIT TO”,因為相比使用海外匯款的方式,日本國內轉賬實際上少了非常多的信息(比如匯款人的住址,電話號碼等),你需要加上這句話,讓Schwab的老頑固放心,給你入金。那麼振込依頼人名一欄最後看起來就會像是這樣:FOR FURTHER CREDIT TO JOHN SMITH 2740-1234。

那麼又出現了一個你可能會碰到的問題,那就是振込依頼人名這一欄最長只能寫48個字符(包括空格和任何標點符號),而有部分的日本銀行,甚至只能允許填寫20個字符,這明顯不夠,所以選擇一家可以允許填寫48個字符的銀行就成為了成功與否的關鍵,好消息是日本幾大銀行都允許填寫48個字符,其中就包括三井住友銀行(SMBC Bank)。

振込依頼人名は何文字まで入力できますか? – 三井住友銀行

 

當然可能你的名字非常長,甚至名字本身就超過了48個字符(這很少見,但是確實有可能),那麼你就需要通過提交添加一個更短的Alias Name申請給Schwab來規避這個問題,具體的操作可以打電話問Schwab的專員索要對應的表格,只要你申請給自己添加一個別名的請求獲得通過,你就可以使用那個更短的名字來執行匯款。

另外一個需要特別注意的問題是,填寫日本國內的匯款單時,都會要求填入フリガナ,你需要一律填寫英文大寫字母。

總結

  1. 去銀行櫃枱使用紙質匯款單實施匯款。
  2. 將十位數的收款賬號拆成兩部分,前七位填在匯款單的收款賬户號碼框上,剩下的三位數寫在收款人名義的前面。
  3. 振込依頼人名填入 FOR FURTHER CREDIT TO YOUR NAME 2740-1234
  4. 匯款單上的所有フリガナ不能出現任何除了英文、阿拉伯數字、標點符號以外的任何字符,特指片假名。

如果你成功了,你就可以僅僅使用大約800JPY左右的成本來解決平時需要花費6000JPY左右的海外送金手續費才能做到的事情。

並且因為是日本國內匯款,對應的匯款都可以在30分鐘內到達Schwab在日本花旗銀行開設的收款賬户上,相比海外送金快則兩個工作日慢則七個工作日的到賬時間明顯快非常多

謝謝閲讀。

 

這篇文章 使用日本國內匯款方式入金Charles Schwab International投資賬户 最早出現於 OXの胡說八道

依云's avatar

HiDPI 配置记录

本文来自依云's Blog,转载请注明。

首先,我是用 X11 窗口系统的,不同屏幕分别设置肯定没戏。所以只好让笔记本电脑的屏幕迁就一下我的4K主屏啦,把笔记本屏幕缩放一下。算一下 scale 值:192 / 120 = 1.6。不是整数,会糊,可总比放大两倍的巨大界面要好。

xrandr --output eDP-1 --scale 1.6 --auto --output DP-2 --auto --pos 3072x0 --primary --fb 6912x2160

这里要注意的是,要指定--pos(或者--panning),不然会重叠;要指定--fb,不然鼠标可能会有部分区域去不了。

然后开始设置。本来我是尝试了一下 KDE 的,但因为我将在下一篇文章中写的原因而放弃,回到了 Awesome。不过也不是全无收获。我把 KDE 的配置方案拿过来用了。你想问怎么拿的?我 btrfs 的文件系统,做好快照再 rsync -n 对比一下它动了哪些文件就有了。

首先是 X11 的资源。在~/.Xresources里写上Xft.dpi: 192,然后xrdb -merge ~/.Xresources一下就好了。顺便再xrandr --dpi 192一下,听说有些程序会读这个。

然后是 GTK。GTK 2 就放弃吧,没办法。文字会按设置的 Xft.dpi 放大,图标啥的不会。GTk 3,要设置两个环境变量:

export GDK_SCALE=2 GDK_DPI_SCALE=0.5

前一个是把界面放大,后一个是把文字缩回去,因为文字已经按 Xft.dpi 放大过,不能再放大一次了。

再然后是 Qt。Qt4 早卸载干净了不用管。Qt5 嘛,也不用管。它自己会处理好。有个按不同屏幕缩放的环境变量QT_SCREEN_SCALE_FACTORS,效果跟 Windows 10 差不多的。但是我为了照顾其它程序已经把屏幕给 scale 过了,就不需要设置这个了。你要设置个QT_AUTO_SCREEN_SCALE_FACTOR=0也行,但这个是默认行为。

最后是个别的程序。

Telegram 直接在设置里关掉「默认界面缩放比例」并且设置缩放比例为 300% 就好了。我也不知道为什么,Telegram 默认的字总是很小。之前 120dpi 的时候我要 200% 缩放,现在 192dpi 需要 300% 缩放了。

YouTube,就是那个网站啦。它其实没什么显示上的问题,只是死活不会给我自动选择 1080p 以上的分辨率。经过仔细二分测试之后发现,把火狐的配置文件夹下的storage/default/https+++www.youtube.com目录删掉之后就好了。没发现删掉这个会有其它影响。

mpv 要修改配置文件,加上no-hidpi-window-scale参数,不然会把视频自动放大,4K视频一打开会只能看到四分之一的画面。加上这个参数,默认窗口大小时,一个视频里的像素会对应一个显示器上的像素,不大不小刚刚好。mpv 文档上说这是 OS X 系统上的默认行为,可我这是 Linux 桌面啊,你把别的平台上的习惯搬过来是几个意思?另外我加了个demuxer-readahead-secs = 20选项。我的大文件都在机械硬盘上,4K 码率又比较高,不多预读一点容易卡。

我的 qemu 之前使用的是-display gtk,也坏掉了。窗口那么大,虚拟机只用左下角那里四分之一的空间。spicy 也有问题,会告诉虚拟机只有 1080p。解决方法是 unset GDK_SCALE GDK_DPI_SCALE。它们在放大了自己的界面的同时,把显示的虚拟机的内容也给放大了,所以干脆叫它们别动。也没什么别的影响。

哦还有 Zoom。设置个QT_AUTO_SCREEN_SCALE_FACTOR=1似乎就好了?我试了一下QT_SCREEN_SCALE_FACTORS,会导致很怪异的行为。

以上解决了显示大小的问题,但我发现还有个问题:我的鼠标光标时大时小的……从 KDE 那边弄来几个设置之后就好了,而且主题也更加一致了呢。

首先是设置 xcursor 环境变量:

export XCURSOR_THEME=Vanilla-DMZ XCURSOR_SIZE=36

听说对应的 X 资源大家都不理睬,那我也就不设好了。

然后是 GTK 2 的~/.gtkrc-2.0文件里写上:

gtk-cursor-theme-name = "Vanilla-DMZ"
gtk-cursor-theme-size = 36

再接下来是 GTK 3 的~/.config/gtk-3.0/settings.ini

[Settings]
gtk-cursor-theme-name = Vanilla-DMZ
gtk-cursor-theme-size = 36

然后又没了。天知道为什么 Qt 那边啥都不干就好好的,GTK 却这么麻烦。

啊,你问这些环境变量在哪里设?我给写~/.xprofile里了。不过这还不够。有些 GUI 程序会由用户的 systemd启动(比如我的 Telegram 是由 systemd 启动的,为了在内存用得太多的时候自动重启),有些 GUI 程序会由 D-Bus 激活(比如 gnome-terminal)。这些是和登录会话分开的,所以要手动导入一下。以下是我的 .xprofile 中导入图形界面相关环境变量的部分:

_envs=(
  GDK_SCALE GDK_DPI_SCALE
  XCURSOR_THEME XCURSOR_SIZE
  XMODIFIERS QT_IM_MODULE GTK_IM_MODULE
  LIBVA_DRIVER_NAME GST_VAAPI_ALL_DRIVERS
)
dbus-update-activation-environment "${_envs[@]}"
systemctl --user import-environment "${_envs[@]}"

至于登录界面怎么办,我是在 lightdm 的 display-setup-script 里,跑了跑 xrandr,设置了一下 Xft.dpi 资源。环境变量啥的没动,反正用不上。当然你也可以去改~lightdm/.pam_environment来设环境变量,反正现在 Arch Linux 还是读它的。别的 dm 同理。

依云's avatar

让 QEMU 使用 SPICE 协议

本文来自依云's Blog,转载请注明。

缘起

我就是买了个4K显示器,咋这么多事呢……(有两篇文章还在路上)

第一个问题是,我的显卡 vGPU 最高只支持 1920x1200 的分辨率。行吧,我缩放成了吧?嘿嘿,-display gtk 的缩放不会保持比例,我只好算了算最大保持比例的大小,然后窗口切成浮动,再调用命令调整到指定的大小:

sleep 1 && xdotool getactivewindow windowsize 3280 2122

然后还要居中放置一下。多麻烦!

第二个问题是,我想在虚拟机里试试 i3,但是我的按键总是会被外边捕获。Super 键基本上是 Awesome 在用,而 Alt 键会撞上这个 GTK 窗口菜单栏的快捷键。

配置

经过多番尝试和摸索之后,确定了如下的参数:

  -display egl-headless,gl=on,rendernode=/dev/dri/renderD128
  -spice unix,addr=/run/user/1000/qemu/ArchKDE/spice.sock,disable-ticketing
  -device virtio-serial-pci
  -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0
  -chardev spicevmc,id=spicechannel0,name=vdagent

当然要gl=on啦,不然我怎么玩特效,我还不如回到 vbox 去呢(啊不,我的 vbox 不喜欢 btrfs,经常出错然后只读挂载,所以已经被删掉了)。要指定rendernode,不然它会 fallback 到 llvmpipe,然后还段错误崩掉……这也是我不-display spice-app的原因。

后边三行是从这个 QEMU + Spice with Copy & Paste 抄来的。搜索问题时不小心遇见,然后解决了这个我一直没有处理的问题。SPICE 不但能共享剪贴板,而且还支持 PRIMARY 选择区呢~虚拟机里要安装 spice-vdagent 并启动相应的服务。

然后是客户端的选择。一个很神奇的地方是,virt-viewer 看上去轻量,实际上只是选项少而已。它不光拖进来个 gtk-vnc 依赖,还把 libvirt 都给我带上了……然后 GNOME 的 vinagre,我不知道为啥,它就是连不上我的 spice+unix 地址。哦对了,virt-viewer 直接敲命令调用也是连不上,只能用 xdg-open 才能正常打开。

virt-viewer 有个依赖叫 spice-gtk。我试着 pacman -Ql 了一下,还真找到个 spicy 工具。比 virt-viewer 轻量多了,选项也更为丰富,比如可以选择不 grab 键盘。

一点额外的东西

在群里听说了 virtio-fs 共享方案,听说比 virtio + 9p 更高效。然后我用它成功取代了之前用的 NFS 方案(反正我的 vbox 虚拟机已经被删掉啦)。NFS 的服务也可以卸载啦(开放一堆端口到公网,看着有点怕怕的,虽然是 IPv6 地址不太会被扫到,但知道我的地址的咋办呢)。

virtio-fs 相比 virtio + 9p 的另一个优点是,和 NFS 一样,virtiofsd 是以 root 权限运行的,所以可以写入我的 pacman 缓存。qemu 那个 9p 似乎没有办法。至于启动嘛,用 systemd abstract socket 触发一下就好了。

另外,我使用 GVT-g 和 virtio 输出视频信号时,均遇到了声音在视频画面变化时声音卡顿的情况。一个绕过的办法是,通过设置 PULSE_SERVER 环境变量以及加载 module-native-protocol-tcp 模块,将音频信号直接通过网络发送到宿主机上,一点也不卡!

RecursiveG's avatar

IPsec 配置备忘 Part8 - iOS 客户端

⚠ 天坑预警 ⚠

我在 iOS 12/13/14 各一台设备上测试过,但是测试的时候不是次次都能工作。由于 iOS 的坑实在太多,以及不同版本的 iOS 的行为都不太一样,如果你碰到了我没碰到的坑我只能祝你好运 (

接 Part6。折腾完 Android 以后我们来折腾一下 iOS。基本原理都是一样的,只是需要把苹果那一套 Vendor-specific 的配置选项翻译成 strongSwan 的,这样才能和服务器上的 strongSwan 互相通信嘛。由于 iOS 上的 IKEv2 客户端不是 strongSwan 而是苹果自己魔改的不知道什么版本,所以坑比起 Android 客户端更多。幸好 strongSwan 项目已经帮我们都踩了不少坑了:见 iOS (Apple iPhone, iPad…) and macOSIKEv2 Configuration Profile for Apple iOS 8 and newer

坑坑坑

由于本 Part 是基于 Part6 的,所以我们先来列举一下会需要我们做调整的 iOS 的坑(大部分在 strongSwan 的文档里已经提到过了):

  1. iOS 的 local_id 和 remote_id 全部都是 FQDN 类型,意味着我们需要按照 Part7 的说明给证书加上dns_name的 SAN。
  2. iOS 不允许 p12 证书使用空密码。
  3. iOS 12 还不支持 ed25519,所以我们需要改用其他算法。
  4. iOS 的 mobileconfig 全是坑。
  5. iOS 需要在配置描述文件显式指定 CA 的 CN 才会发送 CERTREQ。
  6. 你需要在服务端配置文件的 pool 里指定一个 DNS,否则 iOS 连上后无法上网。

基本流程是:生成密钥和证书;生成 iOS 的mobileconfig配置描述文件;想办法把这个文件安装到 iOS 设备上;最后尝试连接。由于 iOS 的配置文件比 Android 的复杂许多,所以我写了个 Python 脚本来负责生成,如果你用 macOS 也可以从苹果下载官方的配置工具。

依云's avatar

Python 小版本升级是怎么 break 已有项目的

本文来自依云's Blog,转载请注明。

近日,Arch Linux 终于开始升级到 Python 3.9 了。很多人认为 Python 小版本升级容易搞坏兼容性,导致项目无法在新的版本上运行。事实是这样的吗?我正好借着 Arch Linux 升级 3.9 的机会,分析一下打包过程中失败的项目到底是出了什么事。

需要说明的是,我仅大致地分析了打包的报错信息,不排除分析出错,或者有额外的问题没有被看见的情况。另外我是在打包过程中随机(arbitrarily)取样,并且排除了我不能确定问题所在的案例。

以下项目测试失败是和 Python 3.9 相关的。排序是按照项目开发者的无辜程度排序的。也就是说,排序越靠前的,我越是认为项目开发者是无辜的;而像「硬编码 Python 3.9 为未发布的版本」这种完全不 future-proof 的做法,现在坏掉了真是自找的。

其中,使用的公开特性变化导致问题的有 3 个,调用私有属性或者方法、依赖非正式的文本信息的有 11 个,使用已废弃的特性的有 8 个,使用已被修复的 bug 的有 2 个,使用未来注定会出问题的信息的有 3 个。总共 27 个。

  • freecad: PyTypeObject.tp_print 没了
  • python-llfuse: PyTypeObject.tp_print 没了
  • linux-tools: PyMODINIT_FUNC 的变化导致了警告,然后被转为错误
  • python-blist: _PyObject_GC_IS_TRACKED不再在第三方库中可用(被公开 API 取代)
  • python-pyflakes: Python 语法解析报告的列位置似乎不太对,应该是受新的语法解析器的影响
  • python-pylint: Python 语法解析报告的列位置似乎不太对,应该是受新的语法解析器的影响
  • python-typing_inspect: 使用私有名称 typing._GenericAlias,结果新版本变成了 typing._SpecialGenericAlias
  • python-sphinx-autodoc-typehints: 看上去是类型标注相关的内部更改移除了 typing.Dict.__parameters__ 属性造成的
  • python-fastnumbers: 看上去是内部函数 _Py_dg_stdnan 不再被默认包含导致的问题
  • python-libcst: 类型标注相关的内部更改移除了 typing.Dict.__args__ 属性造成的
  • monkeytype: typing.Dict 的类型从 type 变成了 typing._SpecialGenericAlias
  • scrapy: 由于 typing.Optional[str] 的字符串表示由 typing.Union[str, NoneType] 变成了 typing.Optional[str] 导致 mitmproxy 运行出错,进而使得 scrapy 的测试失败
  • python-billiard: 调用的私有方法 _posixsubprocess.fork_exec 参数发生了变化
  • python-pytest-benchmark: argparse 的帮助信息格式有优化
  • python-opentracing: 自 3.7 起废弃的 asyncio.Task.current_task 被移除
  • python-engineio: 自 3.7 起废弃的 asyncio.Task.all_tasks 被移除
  • impacket: 自 3.2 起废弃的 array.array.tostring() 被移除
  • python-pybtex: 自 3.2 起废弃的 xml.etree.ElementTree.Element.getchildren 被移除
  • python-jsonpickle: 自 3.1 起废弃的 base64.decodestring 被移除
  • python-ioflo: 自 3.1 起废弃的 json.loads() 参数 encoding 被移除
  • routersploit: 自 Python 3 起废弃的 threading.Thread.isAlive 终于被移除了
  • python-socketpool: 自 Python 3 起废弃的 threading.Thread.isAlive 终于被移除了
  • python-furl: Python 3.9 修正了一处 URL 解析 bug
  • python-stem: Python 3.9 移除了错误的 unittest.mock.__version__
  • python-natsort: Python 的 Unicode 支持更新到了 13.0.0 版本,CHORASMIAN NUMBER ONE 字符被判定为数字,但是测试代码不认识,认为程序出错
  • python-pony: 对新版本的 Python 报不支持的错误
  • python-dephell-pythons: 硬编码 Python 3.9 为未发布的版本,但现在 3.9 已经发布了

而以下项目的测试失败与 Python 3.9 没有直接关系,共 26 个。其中与 Python 生态有关的有 18 个,与其它项目有关的有 4 个,依赖外部信息的有 3 个,包括一个特别搞笑的依赖夏令时是否生效的。

  • python-eventlet: 调用的 dnspython 私有方法已不存在;DNS 解析超时
  • python-markdown2: 语法高亮的结果有少许变化,不符合预期。推测是 pygments 新版本的变化
  • python-flake8-typing-imports: 似乎是 flake8 能够检测到更多的问题了
  • python-babel: 使用了已废弃的特性,测试被 pytest 拒绝
  • python-pygal: pytest 6.1.0 移除了 Metafunc 的 funcargnames 属性
  • python-flask-gravatar: 使用了已废弃的特性,测试被 pytest 拒绝
  • python-pytest-relaxed: 使用了已废弃的特性,测试被 pytest 拒绝
  • python-pytest-randomly 使用了已废弃的特性,测试被 pytest 拒绝
  • python-deprecated: 测试所预期的警告文本信息已经发生变化
  • python-dbus-signature-pyparsing: 执行时间超过了测试设定的 200ms 时限
  • python-tinycss2: flake8 风格检查未通过
  • python-pytest-runner: black 风格检查未通过
  • python-portend: black 风格检查未通过
  • python-aiohttp: @coroutine 的 DeprecationWarning 被视作错误
  • python-poetry: poetry-core 的一项数据由 dict 改为 OrderedDict,使得输出顺序与测试预期的不一致
  • python-isort: 将使用旧版本 isort 的外部项目的 import 排序视为正确,然后它还真出错了
  • python-cachecontrol: Python 2.7 相关
  • python-zc.lockfile: 测试代码把 Python 3 代码喂给了 Python 2.7。可能是该库已经不支持 2.7 了
  • python-occ-core: 依赖 OpenCASCADE 的版本更新,不被支持
  • protobuf: C 整型比较因表示范围问题而恒为假,警告转错误。是因为新版本的 gcc 比较聪明么?
  • gnome-passwordsafe: 构建系统发现有依赖缺失
  • io: C 代码引用了不存在的系统头文件
  • ceph: C++ 相关问题
  • python-distlib: 调用远程 XML-RPC 太多被限制导致预期的数据与实际错位
  • python-requests-toolbelt: 测试所需要的 HTTP 资源 404 了
  • postgresql: 夏令时结束,导致实际时区与预期对不上。「所以冬天就不要滚包啦,冬天要冬眠!」

所以在这些升级 Python 3.9 的项目中,不兼容 Python 3.9 仅仅只占一半,其中又有一半多属于「总有一天会坏掉」的类型(一大半属于「不听话」,使用没有明确文档、预期为私有的特性,少数尝试当预言家但是失败了)。最后剩下的,再一大半是使用了至少两个版本前已经说了要废弃的特性,只有三个莫名地发现自己真的被 Python 坑了,还都是 C API 部分的。

所以我对我自己的脚本顺利升级到 Python 3.9 非常有信心呢。可能有些老代码使用了已经废弃的特性,所以我也设置了环境变量 PYTHONWARNINGS=default,ignore::ResourceWarning 以便及时得到提示。

哦对了,Arch Linux 中受 Python 3.9 升级影响需要更新的软件包共有2077个,绝大部分我都没见着失败的。目前从开始升级到现在已经过去六天,还剩最后40个失败了的包。

's avatar

缪可消失之日

喜欢缪可的人通称缪客。大家都是缪客。

好吧,不是所有人,但是学校里的很多人,我认识的,我不认识的,都喜欢缪可。如果放学后有看到一群人围成一圈,像是在举行神秘仪式的邪教徒一样,那准保是在缪可。有的人喜欢缪可之甚,会在上课偷偷摸摸地缪可,被老师发现叫到后面罚站(现在这个时代肯定不能随便叫学生罚站了)。我们学校甚至有缪可社,不少学生都有加入,他们会隔一个周末,在学校的足球场上举办大型缪可,学生们围成的圈有时候会特别大,大到跑道上。不过有的学生不喜欢大规模的活动(比如我就只会做到放学经过社办门口进去看一看的程度)。所以足球场上的盛景,也是一位缪友告诉我的。

我不太想说,但是有人喜欢自然有人无感,甚至反感。二狗(他说他家里人也管他这么叫,不仅因为他是老二,而且是狗年出生)大概就是其中一位。他就不是很喜欢随大流,他的一句口头禅就是“干嘛学那些人”。我觉得这可能跟他有个优秀的哥哥有关,虽然二狗从来不跟我们过多提这个哥哥。

我问过他:“二狗试试缪可吗?”

他顿了一下,摇了摇头,“最近太忙了,没时间”。我也就没再问过他了。

或许是知道缪可在学校里的人气,学校外面的街角那里,最近还有立缪可的广告牌,特别大的一张喷绘布。广告牌里的明星我虽然不熟悉,但是不得不说,她缪可的样子确实有点酷。

所以今天,我以为也是上上课,课间和朋友聊天的时候提到几句缪可,放学之后稍微缪可一下,这样平凡的一天。我错了。


早上我看群里的消息,都是在讨论这个:缪可公司出了一个大丑闻。因为没有主流媒体报道,我只能从各个地方流传的消息里翻出个一二。虽然我自己对这类丑闻无所谓,但我大概知道,缪可完了。

不过我来到教室后,见到的又是另一番生龙活虎的景象:同学们或多或少都在讨论缪可,连一些就我所知从不缪可的人也加入了讨论。

“唉,缪可是挺好的,可惜被这个公司给毁了。”

“虽然我不是缪客,但是整这么一出,确实是要完了。”

“门口的那个广告牌估计要被砸了。”

我不忍心加入讨论,便拿出书来看,不过关于缪可的声音还是不断地传进我的耳朵。我只好装睡,用胳膊挡住耳朵直到上课铃响。

下课时,有些不缪可的人也会问我一句对这件事的看法,我回道,我能有什么看法,缪可要玩了呗。他们听到便嗤笑一声,继续聊别的了。有人在教室里对那位被罚站的同学大喊一句“今天还在课上缪可吗”,然后一阵哄笑。那位同学只是摇摇头,没说话。

放学后,本来司空见惯的缪可小团体也不见了。我经过缪可社办的时候,发现社办的牌子已经被卸下来,房间里不同寻常地冷清,偌大的房间里只有两个同学,一位在写作业,另一个在看手机,两个人我都有点印象。我问社团怎么了,看手机的同学抬了头望了我一眼,说:“决定解散了。”是嘛,我回答道,然后默默地离开。这是我最后一次进缪可社的房间。

路过街角,那个缪可的广告牌被人砸破了。破洞的位置大致在那位明星的脸附近。周围围了一圈人,有的人说“草”,有的人在拍照。我加快了脚步,想要把这个景象扔在脑后。

我知道从今天起,没有人敢在白天缪可了。

晚上在床上辗转反侧,理性告诉我,别去想缪可的事了,但是我就是止不住地去想,一旦想到,就止不住地难过。我问自己:“缪可本来不该是给人带来快乐的事情吗?为什么我现在这么难过?”

我笑了。我决定忘了它。


几天之后,再去缪可社的时候,大门已经紧锁,门上贴着一张纸:

“虽然我们是喜爱缪可的一群人,但是我们无法容忍缪可公司的所作所为。所以我们决定,在缪可公司给出回复并作出道歉之前,无限期停止缪可社的活动。缪可社”

街角那个被打烂了脸的广告牌,第二天也拆了,只留下空空的铁架子。广告牌上的明星公开道歉,并宣布停止与缪可公司的一切合作。

二狗有问我:“最近还在缪可吗。”

我回答道:“没了。”

“哦。”他说道。然后他就没再问过我了。

RecursiveG's avatar

IPsec 配置备忘 Part7 - 证书 II

接 Part2 与 Part6。尝试折腾一下除了“以证书 DN 作为 id”以外的证书使用姿势,如果有错误或者与本文解释相冲突的情况欢迎在评论指出。

IKEv2 证书认证

在 IKEv2 里,一方需要认证另一方基本只需要两样信息IDAUTH。ID 就是之前配置文件里反复出现的那个,AUTH 一般是使用证书私钥对某些数据的签名,具体细节请参考 RFC。证书则是把两者关联了起来:如果对方给出的 AUTH 能通过证书验证,那么对方就是这份证书所表示的那个人,同时这份证书又是颁发给 ID 的,那么对方就是 ID。

验证者要取得对方的证书有几种方式:

  • 管理员可以直接把证书塞进验证者的/etc目录里
  • 被验证者可以自己把证书发送给验证者(send_cert = always参数)
  • 验证者可以向被验证者请求证书(send_certreq = yes参数)。当然被验证者也可以选择不发送证书(send_cert = never参数),只是验证会失败就是了。

另外如果验证者既不询问证书(send_certreq = no),被验证者也不主动发送证书(send_cert = ifasked),验证一样会失败。

strongSwan 在尝试匹配 ID 和证书的时候会检查 Subject DN 和 SubjectAltName (SAN)。我们之前一直在使用 Subject DN,而 SAN 则允许我们使用域名甚至 IP 作为 ID。另外,虽然我一直称呼“域名”或是“IP”,但是实际上只要 SAN 和 ID 匹配即可,这个“域名”到底是不是我们的并没有关系。(当然只有自签才能签出这种证书)

Subject Alternative Name

要颁发带有 SAN 的证书只需要在 tmpl 文件(参见 Part2)中添加如下内容:

# 域名 SANdns_name="san.hosta.com"# IP 地址 SANip_address = "fd00::1"

需要注意的是,SAN 是区分类型的,比如上文的 DNS 和 IP,而 IKEv2 使用的 ID 也是分类型的(FQDN,IP,etc.)。类型需要匹配才能认证成功。有的教程会使用形如@xxx.xxx.xx.x这样的 IP,可能原因是,某些客户端使用 IP 作为 ID 但是却标记为 FQDN,或者是生成证书的时候将 IP 标记成了域名 SAN。这种情况就需要给 IP 添加前缀@来强制让 strongSwan 将其当作域名 ID。具体的解析规则可见 Identity Parsing 文档。

除了在证书生成上的这些零碎注意点之外,我觉得另一点导致 IKEv2 很难配置的因素是,strongSwan 允许省略很多参数,而不同的平台会给这些被省略的参数提供不同的默认值,导致你以为的设置和程序实际使用的设置出现偏差。更不用说有的平台还有一些稀奇古怪的 BUG。

Android Client w/o DN

这里我们尝试重复 Part6 中的实验,只是不出现 DN,全部用域名代替。同样的,只有有变化的部分才有注释。首先重新生成证书加入 SAN,你只需要重新用 CA 私钥签发服务器证书即可:

1
2
3
4
5
cn = "server common name"
# 域名 SAN,需要和 ID 相匹配
dns_name = "vpn.server.com"
activation_date = "2020-01-01 00:00:00 UTC+0"
expiration_date = "2021-01-01 00:00:00 UTC+0"

然后先看客户端 Profile 的变化:

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
jq -n \
--arg uuid `uuidgen -r` \
--arg name "My server"\
--arg type "ikev2-cert"\
# 注意这里(以及下面的 json 里)去掉了 remote.id。在 Android 客户端上,如果不指定
# remote.id,那么 ID 就默认是 remote.addr 中的值。
#
# 用服务器的域名代替了 IP 地址,目前客户端似乎还是会优先使用解析出来的 IPv4 地址,
# 所以后面 JSON 里的 ipv6-transport 配置也不用了。
--arg addr "vpn.server.com"\
# 这里换成了 CA 证书,注意下方新增了 remote.certreq 配置项。
# 虽然这个配置似乎默认就是 true,不过还是加上以防万一。
--arg servercert "`sed '/-----/d' ca-cert.pem`"\
--arg localbundle "`sed '/-----/d' client-p12bundle.pem`"\
'{
uuid:$uuid,
name:$name,
type:$type,
remote:{
addr:$addr,
cert:$servercert,
certreq: true
},
local: {
p12:$localbundle
}
}' > profile.sswan

主要变化之一是省略了 remote.id 并将 remote.addr 从 IP 改成域名。注意这里不是不设置 ID,而是采用客户端的默认值(remote.addr)。和服务器配置中的省略 remote.id(%any)有巨大不同。之二是使用 CA 证书而不是直接使用服务器证书,至于为什么需要设定 certreq 请参考第一段。

再看服务端配置变化:

server.conf
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
connections {
android-connection {
version = 2
# 由于客户端优先采用 IPv4 解析,这里就需要改成 IPv4 的地址了。
local_addrs = 12.34.56.78
remote_addrs = %any
proposals = aes256gcm128-sha512-x25519
pools = ip4pool
local {
# 和客户端设定的 remote.id 保持一致(
# 恰好是客户端设定的 remote.addr (
# 恰好是服务器的域名
# )
# )
id = vpn.server.com
auth = pubkey
}
remote {
id = %any
auth = pubkey
}
children {
child_sa {
local_ts = 0.0.0.0/0
remote_ts = dynamic
mode = tunnel
esp_proposals = aes256gcm128-sha512-x25519
}
}
}
}

pools {
ip4pool {
addrs = 10.10.10.100-10.10.10.150
}
}

这样我们就配置好使用域名作为 ID 的 strongSwan 服务器了。

RecursiveG's avatar

IPsec 配置备忘 Part6 - Android 客户端

接 Part4,终于要开始准备搭一个能用的 VPN 服务器了。另外,如果你要拿这个配置去翻墙请自便,但是不对实际效果做任何保证,本文只考虑传统的 VPN 使用场景。

基本上,服务端的配置和 Part4 中的一致,这里着重介绍如何配置客户端。配置使用 Play Store 上的 strongSwan客户端,版本为 2.3.1。

服务器配置

首先,这个客户端似乎还不支持 ed25519 证书,因此我们需要改为使用 ecdsa 证书。完整的证书生成操作请看 Part2,在生成私钥时指定--key-type ecdsa即可。现在假设你已经生成好了所有 6 个文件:ca-key.pem,ca-cert.pem,client-key.pem,client-cert.pem,server-key.pem,server-cert.pem

另外,这次我们会在服务器上配置 NAT,所以不再需要手动在服务端配置一个 IP 了。假设服务端eth0接口的公网 IP 是2000::1。首先将ca-cert.pem,server-key.pem,server-cert.pem三个文件移动到服务器上正确的地方。然后编写配置文件,同样的,这里只注释和 Part4 中不同的地方。

server.conf
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
connections {
android-connection {
version = 2
# 服务器的地址。根据官方文档,服务器内核版本需要大于等于 5.8
# 才能支持 Android 客户端使用 IPv6 地址连接
local_addrs = 2000::1
# 接受客户端来自任何地方
remote_addrs = %any
proposals = aes256gcm128-sha512-x25519
# 只分配 IPv4 的虚拟 IP
pools = ip4pool
local {
# 请按照实际情况修改 id
id = CN=server common name
auth = pubkey
}
remote {
id = %any
auth = pubkey
}
children {
child_sa {
# 允许客户端将去往所有目的地的流量发送给服务端
local_ts = 0.0.0.0/0
remote_ts = dynamic
mode = tunnel
esp_proposals = aes256gcm128-sha512-x25519
}
}
}
}

pools {
# 只保留 IPv4 池
ip4pool {
addrs = 10.10.10.100-10.10.10.150
}
}

配置完毕后systemctl restart strongswan

客户端配置

在客户端上,我决定创建一个可以直接导入的 Profile 文件,避免手工输入一大堆地址(要记得我在用 IPv6)。首先需要把 client-key.pem 和 client-cert.pem 合并成一个文件,注意 bash 本身是不支持多行命令和注释穿插写的,在执行的时候需要手动移除注释:

1
2
3
4
5
6
7
8
9
10
11
certtool \
--load-privkey client-key.pem \
--load-certificate client-cert.pem \
# 名称随意
--p12-name "Key Cert Bundle" \
# 空密码
--empty-password \
# 合并成 PKCS12 格式
--to-p12 \
# 需要指定 3des-pkcs12,否则 Android 系统无法正确导入
--pkcs-cipher 3des-pkcs12 > client-p12bundle.pem

新生成的文件是client-p12bundle.pem。然后我们参照 strongSwan 的文档创建可以直接导入应用的 Profile 文件。当然你也可以手动导入证书再手动配置 Profile。

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
jq -n \
# 生成一个随机的 UUID
--arg uuid `uuidgen -r` \
# Profile 的名称,可以随意写
--arg name "My server"\
# 认证类型
--arg type "ikev2-cert"\
# 需要手动指定 remote id,否则服务器的地址将被当作 id,导致认证失败
# 当然你也可以在创建证书的时候就把服务器地址写在证书里,避免手工指定 DN
# 注意:id 和服务器地址和证书有复杂的检验关系,填写不当容易造成各种奇怪的
# 身份验证错误。
--arg remoteid "CN=server common name"\
# 服务器的地址,我这里使用 2000::1 代替了实际地址,使用 IPv4 和域名也是可以的。
--arg addr "2000::1"\
# 嵌入服务器证书,注意这里我直接嵌了服务器本身的证书。嵌 CA 证书应该也是
# 可行的,但是我没试。
--arg servercert "`sed '/-----/d' server-cert.pem`"\
# 嵌入客户端私钥和证书
--arg localbundle "`sed '/-----/d' client-p12bundle.pem`"\
'{
uuid:$uuid,
name:$name,
type:$type,
remote:{
id:$remoteid,
addr:$addr,
cert:$servercert
},
local: {
p12:$localbundle
},
"ipv6-transport": true
}' > profile.sswan
# 根据官方文档,服务器内核版本需要大于等于 5.8 才能支持客户端使用 IPv6 地址
# 连接。同时需要设定 ipv6-transport 为 true。如果你使用 IPv4 则不需要此设置。
# 另外这里没有指定 local.id,根据文档,在没有指定 local.id 的情况下,客户端程序
# 会自动使用客户端证书的 DN 作为 id

最后将生成的profile.sswan文件拷贝到手机上并导入即可。然后试一下能否成功建立连接。

NAT 配置

然后这样配置完了,虽然能连上服务器,但是依然不能访问网络,我们还需要在服务器上配置一下 NAT。一般是一条防火墙的masquerade规则。这里只简单把配置放出来给自己做个备忘,不做细讲:

nftables.conf
1
2
3
4
5
6
7
8
9
10
table ip nat {
chain prerouting {
type nat hook prerouting priority 0;
}

chain postrouting {
type nat hook postrouting priority 100;
ip saddr 10.10.10.0/24 meta oifname eth0 masquerade
}
}
RecursiveG's avatar

IPsec 配置备忘 Part5 - 防火墙配置

记录一下使用到的端口以及需要做的防火墙配置,不一定能覆盖所有场景,如果有漏掉的规则欢迎留言补充。

TL;DR 在两端均放行:

  • UDP 500
  • UDP 4500
  • ESP 协议

最基本的 IKEv2 使用 UDP 500 端口进行通信。当密钥交换完成后,Linux 内核会将加密了的数据包封装在 ESP 中发送。所以整个包结构是:

Ethernet - 外层IP - ESP - 内层IP - <...>

但是我们也知道,所有除 UDP 和 TCP 以外的四层协议都不能很好地穿过防火墙和 NAT,于是给 ESP 套一层 UDP 就是很自然的事情了:

Ethernet - 外层IP - UDP[dport=4500] - ESP - 内层IP - <...>

这层多出来的 UDP 的端口号就被人为规定成 4500 了。另外由于一些我没有搞明白的原因,strongSwan 会在某些情况下使用 4500 进行 IKE 通信,即使并不需要 ESP UDP 封装。

在默认配置下,strongSwan 需要所有三种规则:使用 UDP 500 和 4500 (即使没有 NAT)进行 IKEv2 协商,然后内核发送 ESP 包。如果有 NAT 存在,strongSwan 会使用 UDP 4500 对数据进行封装而不使用 ESP。

如果你不想放行 4500 又不想影响非 NAT 流量,可以在配置文件中设置mobike = no。你也可以使用encap = yes选项在没有 NAT 的环境中模拟 NAT,强制使用 UDP ESP 封装。如果你能确保你的所有 ESP 流量都是 UDP 封装的,那么不放行 ESP 也是可以的。

如果你使用不封装的 ESP,那么你需要在连接两端都放行 ESP 数据包,不然可能会出现奇怪的现象。比如,你必须先从 A ping B,然后才能从 B ping A,直接 ping 不通,之类的。

用于nftables.conf的放行规则如下:

1
2
3
4
5
udp dport 500 accept
udp dport 4500 accept
# 外层 IP 可能是 IPv4 也可能是 IPv6,需要两条规则来放行所有 ESP 包
ip protocol esp accept
ip6 nexthdr esp accept
Jixun's avatar

快捷方式脚本

重装系统后如果没有备份桌面,那么快捷方式就会不见了。

同时,Windows 的检索对开始菜单目录下的快捷方式应该有加速,有时候会搜索不到桌面。

检索快捷方式

做了个简单的脚本快速添加快捷方式到开始菜单里。

Set objShell = Wscript.CreateObject("Wscript.Shell") Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.GetFile(Wscript.ScriptFullName) strFolder = objFSO.GetParentFolderName(objFile) strDesktop = objShell.SpecialFolders("Desktop") strStartMenu = objShell.SpecialFolders("Programs") strStartMenuGames = strStartMenu & "\Games" Sub Mkdir(dir) If Not objFSO.FolderExists(dir) Then objFSO.CreateFolder dir End If End Sub Function StrFallback(a, b) If a <> "" Then StrFallback = a Else StrFallback = b End If End Function Sub CreateShortcutWithIcon(root, dir, exe, name, desc, icon) strProgramFolder = strFolder & "\" & dir strProgramPath = strProgramFolder & "\" & exe Mkdir root Set lnk = objShell.CreateShortcut(root & "\" & name & ".lnk") lnk.IconLocation = StrFallback(icon, strProgramPath) lnk.Description = StrFallback(desc, name) lnk.TargetPath = strProgramPath lnk.WorkingDirectory = strProgramFolder lnk.Save Set lnk = Nothing End Sub Sub CreateShortcut(root, dir, exe, name, desc) CreateShortcutWithIcon root, dir, exe, name, desc, "" End Sub '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''''''''''''''''''''''''''' 快捷方式代码放在下面 ''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' 建立 Steam 快捷方式到桌面 + 开始菜单 CreateShortcut strDesktop, "Steam", "steam.exe", "Steam", "Steam 启动器" CreateShortcut strStartMenuGames, "Steam", "steam.exe", "Steam", "Steam 启动器" ' 建立 osu! 快捷方式 CreateShortcut strDesktop, "osu!", "osu!.exe", "osu!", "osu! 是一个节奏游戏" CreateShortcut strStartMenuGames, "osu!", "osu!.exe", "osu!", "osu! 是一个节奏游戏" ' 建立其他快捷方式 CreateShortcut "目标目录", "子目录名", "可执行文件", "快捷方式名", "描述" CreateShortcutWithIcon "目标目录", "子目录名", "可执行文件", "快捷方式名", "描述", "图标路径"
Code language: VBScript (vbscript)

比较适合经常重装的情况。

's avatar

USTC Hackergame 2020 - Writeup

USTC Hackergame 2020 - Writeup
RecursiveG's avatar

IPsec 配置备忘 Part4 - Virtual IP

第四篇主要讲一下怎么给客户端下发内网 IP ~
Part1 传送门Part2 传送门Part3 传送门

Virtual IP (VIP)

接 Part3,有了 Tunnel 模式以后我们实际使用的 IP 地址就不用受制于机器的实际 IP 了。但是手动给每个客户端手动分配一个地址显然是不切实际的。于是我们可以使用 Virtual IP 功能自动向连入的客户端分配一个内网 IP,就像 DHCP 或者 SLAAC 那样。

场景配置

与之前完全对称的配置不同,使用 Virtual IP 时需要区分服务端和客户端。先在服务端配置将要分配的 IP,然后由客户端发起连接,服务端就会将配置好的 IP 分发出去。我使用 HostA 作为服务端,HostB 作为客户端。HostA 将会给 HostB 分配 IPv4 与 IPv6 各一个。使用的 Virtual IP 段是fd01::100-fd01::20010.10.10.100-10.10.10.150

hosta$ ip -br addreth1         UP             fd00::1/64 fd01::1/128 10.10.10.1/32hostb$ ip -br addreth1         UP             fd00::2/64

和 Part3 相比,HostA 这里有一些与之前不同的地方,一是内部 IP 全部放在了 eth1 上(而不是 lo 上);二是内部 IP 的前缀长度都是最大值;三是增加了一个 IPv4 的内部 IP,用于和分配的 IPv4 Virtual IP 通信。同时 HostB 也不再手工分配fd01开头的内部 IP 了,将由 strongSwan 自动配置。

当连接建立后,我们应该能看到在 HostB 的 eth1 上出现两个新的,自动分配的 IP。且分配到的 IPv4 地址与 10.10.10.1 之间的通信是加密的,分配到的 IPv6 地址与 fd01::1 之间的通信是加密的。

配置文件

配置文件基于 Part3 修改而来,有变化的部分已添加注释:

hosta.conf
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
connections {
conn_hosta_hostb {
version = 2
local_addrs = fd00::1
remote_addrs = fd00::2
proposals = null-sha-modp2048
# 指定要使用的 IP 池
pools = ip6pool,ip4pool
local {
id = CN=HOSTA_COMMON_NAME
auth = pubkey
}
remote {
id = %any
auth = pubkey
}
children {
child_sa {
# Local TS 加上新增的 IPv4 地址
local_ts = fd01::1/128,10.10.10.1/32
# Remote TS 设置为 "dynamic"
# strongSwan 会自动将其替换成分配出去的 Virtual IP
remote_ts = dynamic
mode = tunnel
esp_proposals = null-sha-modp2048
}
}
}
}

# 定义 IP 池
pools {
# IP 池的名称
ip6pool {
# 池中的 IP,也可以使用 fd01::100/120 这样的表示法
addrs = fd01::100-fd01::200
}
ip4pool {
addrs = 10.10.10.100-10.10.10.150
}
}
hostb.conf
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
connections {
conn_hosta_hostb {
version = 2
local_addrs = fd00::2
remote_addrs = fd00::1
proposals = null-sha-modp2048
# 向服务器请求 VIP,0.0.0.0 代表任意 IPv4
# :: 代表任意 IPv6
vips = 0.0.0.0,::
local {
id = CN=HOSTB_COMMON_NAME
auth = pubkey
}
remote {
id = CN=HOSTA_COMMON_NAME
auth = pubkey
}
children {
child_sa {
# 类似 HostA 的 Remote TS,dynamic 会被自动替换
# 成从服务端接收到的 VIP
local_ts = dynamic
# 增加服务器的 IPv4 内网地址
remote_ts = fd01::1/128,10.10.10.1/32
mode = tunnel
esp_proposals = null-sha-modp2048
}
}
}
}

链接测试

与 Part3 中的对称链接不同,这次你必须从客户端发起连接,也就是从 HostB 执行如下命令:swanctl -i -c child_sa。连接建立后,你应该就能看到自动分配的 IP 了:

hostb$ ip -br addreth1         UP             fd00::2/64 fd01::100/128 10.10.10.100/32

此时再从 HostB 分别 ping fd00::1fd01::110.10.10.1,你应该能看到前者没有加密,而后两者的 ICMP 包被包在了fd00::1 <---> fd00::2的 IPv6-ESP 包里:

RecursiveG's avatar

[娱乐向] Samsung 980 Pro RAID 性能测试

三星的新 SSD 980 Pro 已经发售一段时间了,前两天看到 Amazon 上 1TB 有货,赶紧下单两条。今天到货,于是装机测一波速度。打算用来做系统盘,所以自然就是用来组 RAID-0 咯。于是用 fio 分别测试了一下 btrfs 和 mdadm 组 RAID 的性能。测试完全不严谨,参数是用脸滚键盘滚出来的,放出来仅供各位一乐,为什么是娱乐向你看数据就知道了。同时拉上了打酱油的 960 Evo(我现在的系统盘),以及完全是搞笑用的/dev/shm

场景连续读取(MiB/s)连续写入(MiB/s)4K 随机读取(kIOPS)4K 随机写入(kIOPS)
980 Pro 单盘 EXT463704725330125
980 Pro 单盘 BTRFS6264272013763.7
980 Pro mdadm RAID-0 EXT4121869426298115
980 Pro BTRFS RAID05937393213363.3
/dev/shm62905541792571
960 Evo 单盘 XFS296341019931.3

Emmmm, mdadm RAID0 比内存快……这很合理……以及 btrfs 你的 RAID 性能还能更烂一点吗?

测试用设置在此,测试/dev/shm的时候关掉了direct以及把文件大小改成了1g:

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
[global]
ioengine=libaio
direct=1
time_based
runtime=60
numjobs=1
iodepth=32
group_reporting
size=10g

[seq_read]
stonewall
bs=1M
rw=read

[seq_write]
stonewall
bs=1M
rw=write

[random_read]
stonewall
bs=4k
rw=randread

[random_write]
stonewall
bs=4k
rw=randwrite
RecursiveG's avatar

IPsec 配置备忘 Part3 - Tunnel 模式

第三篇简单介绍一下 IPsec 的 Tunnel 模式,没看过前两篇的快去看~
Part1 传送门Part2 传送门

Tunnel 模式简介

前两篇中我们使用的都是 Transport 模式,但是实际使用中,更常用的是 Tunnel 模式。Transport 模式只加密四层及以上数据,而不修改 IP 头,原始的 IP 头将会原样传输。这意味着我们只能进行点对点传输,因为只有一个 IP 头,我们无法告知对方服务器我们实际要访问的地址。Tunnel 模式则是连原始的 IP 头也一起加密,然后再在前端添加一个新的 IP 头,这样服务器在收到数据包后,可以解密并读取内部的 IP 头,再转发给实际的目标服务器。

配置场景

这次的场景在 Part2 的基础上略有改动:在 HostA 与 HostB 的lo接口上分别添加fd01::1/64fd01::2/64:

hosta$ ip -6 -br addrlo               UNKNOWN        fd01::1/64 ::1/128eth1             UP             fd00::1/64 [--omit--][--omit--]hostb$ ip -6 -br addrlo               UNKNOWN        fd01::2/64 ::1/128eth1             UP             fd00::2/64 [--omit--][--omit--]

在没有建立连接的情况下,fd00::1 和 fd00::2 可互 ping,fd01::1 和 fd01::2 不可互 ping。在建立连接后,fd00::1 和 fd00::2 可互 ping,但是不加密,fd01::1 和 fd01::2 可互 ping 且流量加密。

配置文件

配置文件也是在 Part2 的基础上改动而来,变化部分已加注释

hosta.conf
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
# hosta
connections {
conn_hosta_hostb {
version = 2
local_addrs = fd00::1
remote_addrs = fd00::2
# 使用 NULL 加密以方便抓包观察
proposals = null-sha-modp2048
local {
id = CN=HOSTA_COMMON_NAME
auth = pubkey
}
remote {
id = %any
auth = pubkey
}
children {
child_sa {
# Traffic selector 改为 fd01::*
# 这意味着 fd00::* 不被选中,也就不会被加密
local_ts = fd01::1/128
remote_ts = fd01::2/128
# 传输模式改为 Tunnel
mode = tunnel
esp_proposals = null-sha-modp2048
}
}
}
}
hostb.conf
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
connections {
conn_hosta_hostb {
version = 2
# 交换 local 与 remote
local_addrs = fd00::2
remote_addrs = fd00::1
proposals = null-sha-modp2048
local {
# 使用 HostB 的 id
id = CN=HOSTB_COMMON_NAME
auth = pubkey
}
remote {
id = %any
auth = pubkey
}
children {
child_sa {
# 交换 local 与 remote
local_ts = fd01::2/128
remote_ts = fd01::1/128
mode = tunnel
esp_proposals = null-sha-modp2048
}
}
}
}

链接测试

启动 strongSwan 和 Wireshark,在 HostA 上,可以 ping fd00::2 但是不能 ping fd01::2。然后用sudo swanctl -i -c child_sa建立连接,依然可以 ping fd00::2 但是数据不加密,同时能够 ping 通 fd01::2 了。抓包可以看出明显的ETHERNET-IP-ESP-IP-ICMP的包头层次,并且外层 IP 使用 fd00::* 进行数据传输,内层 IP 使用 fd01::* 的实际目的地址:

RecursiveG's avatar

IPsec 配置备忘 Part2 - 证书

作为系列的第二篇文章,讲解基本的证书原理和配置方法。没看过第一部分的快去看~
传送门:IPsec 配置备忘 Part1

证书认证基础

我们在 Part1 中看到,PSK 认证的基本思路是使用一个只有通信双方才知道的暗号,如果能确认对方确实知道这个暗号,那么认证就成功了。证书认证的思路非常不同:假设 A 需要向 B 证明自己的身份,同时 A 知道 B 信任 C,那么 A 可以向 C 索取一份“介绍信”,当 B 询问 A 的身份时,A 可以向 B 展示这份 C 出具的“介绍信”,如果 B 能够确认这份“介绍信”确实是由 C 出具的,那么认证就成功了。注意这个认证是单向的,假设 A 也信任 C,那么 B 也可以通过向 C 索取“介绍信”来向 A 证明自己的身份。在 PKI 体系中,A 和 B 持有各自的“私钥”,C 作为 Certificate Authority (CA) 向 A/B 颁发证书(即“介绍信”)。同时,CA 也会向自己颁发一份证书并分发给 A/B,A/B 使用 CA 的证书来确认 B/A 出示的证书确实为 C 所颁发。

RecursiveG's avatar

IPsec 配置备忘 Part1 - IKEv2 基础

俗话说得好,配置 IPsec 隧道只有零次和无数次,在被 strongSwan 折磨了 N 次以后,我终于决定要把之前试过的配置都记录下来,于是就有了这个系列。我计划基本上每个 PART 会介绍一个(或几个)特定场景下的配置,配置文件样例以 strongSwan vici 为主,之后可能会介绍 iOS, Android 或者是 Mikrotik 路由器的配置方法,如果我能坚持不鸽写到那里的话(画外音:你这 FLAG 立得……) FLAG 回收了。如果各位有想看的配置场景欢迎留言告诉我,会考虑先写。

IKEv2 与 IPsec 基础

严格来说 IKEv2 不是 VPN,它的全称是 Internet Key Exchange,只是一种用于交换密钥的协议罢了。密钥在计算机里一般就表示为一串固定长度的二进制数据,密钥交换就是指在两台设备之间约定一个相同的二进制串,就像两个密友之间约定暗号一样。一旦密钥交换完毕,IKE 的使命就结束了,具体怎么用约定好的密钥加密数据不是 IKE 解决的问题。在 Linux 系统上,实际的数据包加密解密是由内核的 XFRM 框架负责的,你可以使用ip xfrm命令看到配置好的密钥以及加解密使用的算法。事实上,不使用 IKEv2 而完全手动“交换”密钥是可行的,比如朴素VPN:一个纯内核级静态隧道。你可以看到作者直接使用ip xfrm {policy,state} add指令设定密钥,然后内核就会自动用设定的密钥加密流量。

然而,手动管理内核状态是复杂的,人工分发密钥也不怎么安全,这时就轮到 strongSwan 登场啦(或者说,任何实现了 IKE 的 Daemon 服务)。两台服务器的 strongSwan 使用 IKEv2 协议交换密钥,解决了密钥分发的问题。随后 strongSwan 会把交换得来的密钥设定进内核,这样内核就会自动加密指定的流量了。

从数据包层面上看,IKE 是7层协议,密钥交换使用特殊的 UDP 包完成。而一般被加密的数据包会使用 ESP 封装,ESP 头一般紧跟在 IP 头后。ESP 也可以被封装进 UDP 用以穿越 NAT。

没有 TUN 设备

内核 XFRM 的工作方式和基于 TUN 设备的 VPN 很不一样。一般基于 TUN 的 VPN 会加密所有进入 TUN 设备的流量,因此你可以直接使用路由表来控制哪些流量走 VPN,哪些不走。而 XFRM 的匹配基于策略(i.e. 源地址+目标地址+一些别的),如果某个数据包匹配到了一个策略,这个数据包就会根据这个策略指定的方式被加密。

比方说有A [fd00::1]B [fd00::2],如果你从 A 发送一个数据包到 B,普通情况下这个数据包是明文的。如果你在 A 配置了src=fd00::1,dst=fd00::2,encrypt=<...>的策略并再发一个数据包,这个包就会自动被加密。B 收到了这个数据包,但是它并不知道该如何解密,所以你必须同时在 B 配置一条src=fd00::1,dst=fd00::2,decrypt=<...>的策略,这样 B 才能解密。对于从 B 到 A 的流量也需要类似的两条策略。使用 IKEv2 的话,这些策略 strongSwan 都会自动帮你设置好,无需操心。于是你会发现,尽管我们仍然在使用节点本身的 IP,但是流量却已经被加密了。

对于那些必须使用路由表或是策略匹配不是很有效的场景, Route-based IPsec VPN 也是存在的。我也许会在未来的某一期讲。

依云's avatar

让 Arch Linux 系统和最新的镜像同步,从最快的镜像下载

本文来自依云's Blog,转载请注明。

Arch Linux 就是要追新!要追新自然要选择一个更新及时的软件仓库镜像啦,比如国内的 TUNA、USTC 同步都很及时。但是呢,这俩难兄难弟最近一段时间有些吃不消了,导致下载包的时候很慢,甚至超时失败,使用体验真糟糕。如果直接用上游镜像,比如 pkgbuild.com,漂洋过海的,也挺慢的。

而国内另一些镜像,比如网易腾讯云阿里云华为云,他们要么有 CDN,要么线路很好,下载速度飞快。但是呢,他们基本上每天才同步一次,阿里云还时不时连续数天都没能同步成功,这让喜欢追新的 Arch Linux 用户多不舒服呀。当群里的小伙伴们都用上了最新版本的软件,体会到了让人心痒痒的新特性和 bug 时,你 -Syu 却是「今日无事可做」,真是扫兴呢。

和最新的镜像同步,从最快的镜像下载,真的不可兼得吗?

非也。只需要稍微配置一下,用上我的 pacsync 脚本,就可以啦~

配置方式是,为 /etc/pacman.d 下的镜像列表文件创建一个.sync后缀的同名文件,里边指定用于同步的镜像,而不带.sync后缀的文件里按优先级列出多个镜像。pacman 在下载文件时,会按顺序依次尝试列出的镜像,如果遇到更新不及时 404 的时候,就会尝试另一个。这样,可以仅在下载快的镜像里还没有需要的包文件时,才转而从比较慢的镜像下载。

而需要同步 pacman 数据库的时候,使用pacsync脚本取代pacman -Sy。脚本会使用 bind mount 用.sync文件取代不.sync的版本,就能同步到最新的数据库了。原来的pacman -Syu命令要拆开来用,先pacsyncpacman -Su了。

脚本里使用了单独的挂载空间并且将挂载改为了私有,所以并不会影响到外边。

依云's avatar

tar 归档的权限问题

本文来自依云's Blog,转载请注明。

一次系统升级之后,我的许多 Python 程序突然开始报错:

[...]
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2762, in _get_metadata
    for line in self.get_metadata_lines(name):
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1415, in get_metadata_lines
    return yield_lines(self.get_metadata(name))
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1405, in get_metadata
    value = self._get(path)
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1609, in _get
    with open(path, 'rb') as stream:
PermissionError: [Errno 13] Permission denied: '/usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/PKG-INFO'

WTF 滚坏了!立即回滚!

回滚完之后,我开始调查这个事件——因为 [archlinuxcn] 的这个包是我管的呀。而且我记得之前也遇到过一次类似的情况,当时没有深究。

检查一下软件包里的文件的权限:

>>> tar tvf python-telethon-1.17.4-1-any.pkg.tar.zst | grep PKG-INFO
-rw------- root/root      3659 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/PKG-INFO
>>> tar tvf python-telethon-1.17.4-1-any.pkg.tar.zst | grep -- ----
-rw------- root/root      3659 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/PKG-INFO
-rw------- root/root     12078 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/SOURCES.txt
-rw------- root/root         1 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/dependency_links.txt
-rw------- root/root        27 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/requires.txt
-rw------- root/root        15 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/top_level.txt

好奇怪,Telethon 这些包信息文件怎么只让 root 读了呢?

从 PyPI 上下载 Telethon 的原始 tar 归档回来看看,发现最近几个版本里,文件权限全部只有自己可以读(-rw-------),而所有者是 u0_a167/10167。开发者突然在 Android 系统上打包了呢……安装的时候,部分文件的权限被保留了下来(Arch Linux 打包时强烈反对使用 root 权限执行,因此我用 devtools 打包,解包部分自然是普通用户操作的,所有者无法被保留)。

然后我又看了一下之前的版本,哦豁,所有者成开发者的 id 了,但是有三个版本的 pyc 文件,还有好几个 pyc 文件都是 -rwxrwxrwx。大概系统上的低权限用户可以去改改,然后看谁跑 Telegram 机器人就拿谁的权限?

经过跟开发者的讨论,最终干掉了 pyc 文件,也不在 Android 上打包了。777 权限问题还待解决。不过我更在意的是,为什么会发生这种状况呢?setuptools 干嘛不修一修呢?别的工具创建的用于发布的 tar 归档会不会有类似的问题呢?

结果找了找,发现 setuptools 前年就有人报告这个问题,但是并没有解决。行吧,我打包时统一修正一下权限好了……

下一个 GitHub 生成的 tar 归档看看?咦,-rw-rw-r-- root/root,是处理过了么?啊对,git archive 生成的包是怎么样的?去试了试,原来一样的啊。看来 git 想到了这个问题并且处理了,只是 002 的 umask 有点意外。

Arch Linux 为了普通用户打出文件为 root 所有的 tar 归档使用了 fakeroot,那么 git 是怎么实现的呢?翻了翻代码,git 是自己生成 tar 文件的,写死了所有者是 root/root,但是权限位还是有专门的 umask,默认是 002。可以配置,比如git config --global tar.umask user一下,就会取当前 umask 作为 tar 归档里文件的 umask 了。

至于传统的 GNU autotools 构建系统创建的 tar 归档,我也创建了一个看了一下,并没有特殊处理,跟手动跑 tar 一样。

RecursiveG's avatar

Linux + Windows 10 多系统安装 U 盘

Update: Windows 的引导程序似乎有些问题,如果在同一块 U 盘上写入多个 ISO 分区的话,似乎引导会错乱,最终启动的安装程序版本不是引导程序所在分区的版本。所以暂时一个 U 盘还是只能放一个 Windows 版本。垃圾巨硬。

日常折腾中总免不了要用 LiveCD 修理一下系统,或者是重装一下 Windows 之类的。这时候制作一个引导用的 U 盘基本是最方便的选项了。有不少工具都能创建 U 盘引导,比如 ArchLinux 的 ISO 镜像可以直接用dd写入,Windows 的安装盘也能用 Rufus 创建。不过在使用上还是有些不便,比如dd会覆盖整个U盘,在ISO之外不能再存储其他文件。Rufus 只能在 Windows 上运行,而且一只 U 盘也只能放一份 ISO。于是尝试搞明白怎么把 Linux 的 LiveCD 和 Windows 的安装 ISO 写入到同一只 U 盘就很有必要了。

我个人使用的设备都支持 UEFI,所以这里制作的启动盘也只支持 UEFI 启动,需要 MBR 模式启动的读者请往它处寻。当然,Secure Boot 是要关掉的。制作过程我使用 Linux,纯 Windows 用户现在也可以退出了。基本上,我们需要创建一个 EFI 系统分区(EFI System Partition, ESP),其中包含基本的引导程序(Grub2)和 Linux LiveCD 的 ISO 文件。由于 Windows 的安装程序无法以 ISO 形式被引导,因此我们需要给每个 Windows ISO 文件创建一个分区,并将 ISO 中的内容解压进去。但是分区一旦创建不像文件那么好修改,所以创建每个 Windows ISO 分区的时候我都留了一些额外空间,以备以后 ISO 大小变化,这也意味着这些空间就基本浪费了。That's sad but I guess it's how things work.

另外,购买一个优质的 U 盘还是有必要的,不然不管是创建启动盘还是安装系统都会慢得让你痛不欲生。建议用之前先给 U 盘测一下速,什么拷贝速度只有 2MB/s 的金士顿可以直接进垃圾桶了。至于 U 盘大小取决于你要放多少个 ISO 文件和多少个 Windows 分区,一般 Linux 镜像大小在 500MB~3GB 的都有,Windows 10 的分区一般每个需要 5~6GB.

farseerfc's avatar

關於 swap 的一些補充

上週翻譯完 【譯】替 swap 辯護:常見的誤解 之後很多朋友們似乎還有些疑問和誤解,於是寫篇後續澄清一下。事先聲明我不是內核開發者, 這裏說的只是我的理解, 基於內核文檔中關於物理內存的描述 ,新的內核代碼的具體行爲可能和我的理解有所出入,歡迎踊躍討論。

誤解1: swap 是虛擬內存,虛擬內存肯定比物理內存慢嘛

這種誤解進一步的結論通常是:「使用虛擬內存肯定會減慢系統運行時性能,如果物理內存足夠爲什麼還要用虛擬的?」 這種誤解是把虛擬內存和交換區的實現方式類比於「虛擬磁盤」或者「虛擬機」等同的方式, 也隱含「先用物理內存,用完了之後用虛擬內存」也即下面的「誤解3」的理解。

首先,交換區(swap) 不是 虛擬內存。操作系統中說「物理內存」還是「虛擬內存」的時候在指程序代碼 尋址時使用的內存地址方式,使用物理地址空間時是在訪問物理內存,使用虛擬地址空間時是在訪問虛擬內存 …

RecursiveG's avatar

在 Raspberry Pi 4B 上安装 ArchLinux


很久之前就买了一个树莓派,不过一直在吃灰,正好最近有空就再拿出来折腾一下。原装系统是 32 位的,那么就必定要换一个 64 位的啦,不然对不起这 64 位的 CPU 呀。秉承“Arch大法好”的理念,我就决定用 Archlinux ARM 了。我非常建议先用原版系统更新 Bootloader 和 EEPROM到最新版本。这样可以避免各种奇怪的 bug 和使用一些新加入的功能,比如从网络启动什么的。

ArchLinux ARM 其实已经提供了树莓派的安装教程,基本上只要跟着做即可,我用的是 AArch64 镜像,并且把根文件系统从 ext4 换成了 f2fs,希望在 SD 卡上能有一点点加成效果。装完以后发现串口没有输出,自然不能忍,继续折腾。RPi 4B 一共有两个串口控制器,一个 PL011,另一个被称作 MiniUART。默认情况下,PL011 连接到蓝牙模块,并且 MiniUART 被禁用,但是我们可以通过 config.txt 加载 dtb overlay 来调整。一些常见的配置有:

  • 启用 MiniUART 串口,PL011 继续负责蓝牙
  • 禁用蓝牙,让 PL011 负责串口通信
  • 启用 MiniUART,让 MiniUART 负责蓝牙,PL011 负责串口

但是 ArchLinux ARM 使用 U-Boot 来启动内核,并不遵循 config.txt (╯°Д°)╯ ┻━┻

那么我们只能把 U-Boot 干掉了 (<ゝω・)☆

使用 MiniUART 作为串口

SD 卡的 \boot 目录里需要这么7个文件,RPi 4B 的 bootloader 才好启动 Linux 内核:

  • config.txt: 主要配置文件,uboot-raspberrypi 有提供,但是我们手写。
  • start4.elffixup4.dat: 第二阶段 Bootloader, 由 raspberrypi-bootloader 包提供。
  • rpi4.dtb: RPi 4B 的 Device Tree 文件, linux-aarch64 包中提供了一个基于上游代码的,位于 /boot/dtbs。但是我试了几次都不能正常启动,所以还是从 Raspberry Pi 官方的 Github 下载了一份。
  • Image: 内核可执行文件,由 linux-aarch64 包提供。
  • initramfs-linux.img:由 mkinitcpio 程序生成。
  • cmdline.txt: 内核参数文件,手写。

那么直接上配置:

1
2
3
4
5
enable_uart=1                               # 启用 MiniUART
kernel=Image # 指定内核文件的名称
arm_64bit=1 # 要求以 64 位模式启动,否则默认是 32 位
device_tree=rpi4.dtb # Github 上的 bcm2711-rpi-4-b.dtb 文件,我改了个名
initramfs initramfs-linux.img followkernel # 指定 initramfs 文件

start4.elffixup4.datcmdline.txt 都是原名,就无需写进 config.txt 里了。

1
console=serial0,115200 root=PARTUUID=e10d384f-02 rootfstype=f2fs rootflags=rw elevator=deadline audit=0 rootwait
  • PARTUUID 需要改成你自己的,可以用 sudo blkid 查看
  • rootflags f2fs 似乎默认以只读挂载,会导致没有办法登录。
  • audit=0 关掉 audit,否则内核信息撒得满地都是。

全部折腾完以后把 SD 卡塞进树莓派,应该就能在串口看到登录界面了。

使用 PL011 作为串口

我在安装的时候碰到一个 MiniUART 的 BUG,串口的 Baudrate 不对,内核输出一片乱码。可以尝试使用 PL011 作为串口,也可以升级内核解决。使用 PL011 需要在 boot 分区里加一个新的文件

  • overlays/disable-bt.dtbo: 需要从 Github 上下载,用来禁用蓝牙,并且让 PL011 负责串口通信。

同时需要修改 config.txt 加上 dtoverlay=disable-bt 以启用。此时 enable_uart=1 不再是必要的了。

登录之后

登录之后建议先把 uboot-raspberrypi 卸了,然后 dhcpcd 连上网 pacman -Syu 一下,再重启确认一下启动过程都正常。之后就是标准的 ArchLinux 服务器配置过程:时区, 网络, 防火墙, etc. 搞定以后我们就有一台 AArch64 服务器了。

暂时没有 GUI 的需求,相关的配置就留到下次再折腾了。

's avatar

编写 Minecraft 玩家用小康文本编辑器的努力

(不支持移动版浏览器)

这个编辑器在 这里,大家现在就可以使用了。

虽然说这玩意确实还没做到令我满意的程度,但是我把它写出来了,并达到了勉强可用的程度。

不过开发这玩意,我算是明白 HTML 的富文本编辑器(虽说我这是「小康文本编辑器」,因为屏蔽掉了所有 Minecraft 不支持的样式)有多难开发了。

顺便开发 Web 富文本编辑器的前端工程师们,你们都是好样的!

为什么要造轮子?

Minecraft 原生就支持那么几种样式和颜色,所以一开始记忆样式代码也不是太困难的事情。但是在 Minecraft 1.16,我们有了 RGB 文本支持;而 EssentialX(一种被各大服务器广泛使用的辅助性插件)也积极跟进支持,追加了玩家使用 RGB 颜色的样式代码;但要在脑海里演算 RGB 颜色数值发展的具体变化,感觉还是有点困难,况且我还希望在游戏中实现彩色渐变文本等酷炫的特效。

那么,头脑一热的我决定,干脆自己写个编辑器好了,顺便练练手。

contenteditable

这玩意 其实是个相当历史悠久的东西,IE 5.5 就支持了,而且设计上就是用来开发富文本编辑器的。

在前期开发阶段,我感觉要把编辑区的内容转换为 EssentialX 文本格式应该不是太困难的事情。我的做法是这样的:

  1. 读取 DOM 结构
  2. 获取各 HTML 元素计算过后的 CSS 样式
  3. 内部维护一个样式树(抽象的数据结构),记载每一段文本的样式。例如下面这段文本:

凤凰卷是一种奇怪的点心。

它的 HTML 代码是:

<b>凤凰卷</b>是一种<i>奇怪的</i>点心。

通过计算得出的样式树:

char color bold italic underline strickthrough
凤凰卷 #000000 true false false false
是一种 #000000 false false false false
奇怪的 #000000 false true false false
点心。 #000000 false false false false

然后,我依据这个数据结构,就可以非常轻松的计算出对应的 EssX 文本代码:

&l凤凰卷&r是一种&o奇怪的&r点心。

事情到这里,还是很简单的,毕竟富文本编辑器的操作,本质上不就是操作 HTML 结构嘛(虽然是这样)。

拉胯的 execCommand

这玩意 其实设计上是与 HTML contenteditable 配合使用的,目的就是用来在页面编辑中给选择的内容设置不同的样式。

于是我兴高采烈的用上了,然后发现一些问题:

  • MDN 显示,这玩意过时了。
  • 不同浏览器生成的 HTML 代码不一致,添加的标签五花八门,甚至连早已淘汰的 <font> 标签都出来了。不过还好对于我的解析方案不是太大的问题……
  • 不同浏览器的行为存在差异。例如给文本同时添加下划线和删除线,Chrome 浏览器肯定会把你选择的这段文本旁边的文本的下划线 / 删除线样式搞乱,但 Firefox 浏览器就不存在这个问题。(顺便最后的不完美之处还是跟下划线和删除线有关)

其实如果说 execCommand 这玩意,市面上倒是还有一大票编辑器还在用,而且大概浏览器厂商也不会那么轻易的干掉古董的 API(说的就是你,document.all)。不过考虑到下划线和删除线的问题,我还是决定:换掉它,避免未来更麻烦的事情发生。

用现代的 API 来解决吧!

我重新整理了一下思路,发现我需要的样式操作工作流其实是这样的:

  1. 将用户选择的文本片断剪下来
  2. 分析剪下来的片段的样式,并生成样式树
  3. 直接对样式树进行操作
  4. 从样式树生成相应的 HTML
  5. 将生成的 HTML 追加到文本被剪切的地方

我选择直接操作样式树的原因是因为我只关心最终呈现的效果,这样就可以保证 HTML 元素的低嵌套性(指一个 <span> 标签中包含了所有应有的样式,而不是一层套一层的标签),提升处理效率和鲁棒性。

那么,首先在进行一个样式操作以前,需要把选区剪切下来:

/**
 * 剪切用户选区
 * @returns 剪切下来的用户选区的文本数据结构
 */
cutSelection(): StringItem[] {
    // 获取用户选择的文本范围
    const selection = document.getSelection() as Selection;
    // 如果用户选择了至少一段文本,就取第一段被选中的文本(目前只有 FireFox 浏览器支持一次性选择多个选区?)
    if (selection.rangeCount > 0) {
        const range = selection.getRangeAt(0);
        // 把选区剪切为 HTML 片段
        const extracted = range.extractContents();
        // 进行解析,返回解析得到的样式树
        return this.parseFromFragment(extracted, parent || undefined);
    }
    return [];
}

然后拿到样式树以后,再进行步骤 3-5 就可以了。

但是……接下来的不可靠因素出现在剪下来的 HTML 片段(DocumentFragment)。当用户选择一段文本,有这么几种特别需要注意的情况:

  1. 用户只选中了一个元素里的文本,则整个片断里只有一个文本节点(Node)。
<b>Phoenix Roll</b> is <i>awesome</i>
   | ----+--- |
         |
         +-------------------- [0] [Text Node] Phoenix Roll
  1. 用户选中了嵌套在多层元素中的文本,则:
<u><b>Phoenix Roll</b> is <i>awesome</i></u>
          | ----+------ |
                |
                +-------------------- [0] [Element Node] <b>nix Roll</b>
                                      [1] [Text Node]     is

没错,外面一层的 <u>(下划线)标签就被完全无视了。虽说这样的操作其实合情合理,毕竟浏览器哪里知道你想要的根元素是哪个啊。

下划线和删除线

我一开始的考虑是:让顶层的 CSS 样式直接覆盖父级元素的 CSS 样式,这样不仅处理容易,而且展示效果也会是正确的。但是,下划线和删除线样式的处理问题又向我泼了一盆冷水。

CSS 的下划线和删除线的样式继承偏偏和别的不!一!样!

简而言之,CSS 的 text-decoration 实际渲染样式是从父级元素向下叠加,就像这样:

<s>
    <!-- 追加了删除线样式 -->
    <u>
        <!-- 追加了下划线线样式 -->
        <span style="text-decoration: none">
            <!--
            期望效果:这段文字什么样式都没有
            实际效果:这段文字有下划线和删除线样式
            -->
            为什么我的下划线和删除线没有被消除?
        </span>
    </u>
</s>

那么,如果要正确的获取到一段文本的下划线和删除线样式,就只有向下递归搜索了:

/**
 * 检查某个元素是否会渲染出下划线或删除线样式
 * @param el 要开始搜索的节点
 * @param root 根节点(停止搜索的地方)
 * @param name 搜索的关键词
 * @returns 是否会渲染
 */
static searchLineStyle(el: HTMLElement, root: HTMLElement, name: string): boolean {
    // 如果被搜索的节点没有有效的根节点,停止搜索
    if (el === null) {
        return false;
    }
    // 获取该元素实际计算出的样式。直接通过 `el.style` 未必能够获取到实际计算出的样式。
    const styles = window.getComputedStyle(el);
    // 如果找到了搜索的关键词,停止搜索,返回搜索成功
    if (styles.textDecorationLine.includes(name)) {
        return true;
    }
    // 如果要开始搜索的节点已经是根节点,停止搜索
    if (el.isEqualNode(root)) {
        return false;
    }
    // 递归,继续搜索上一层元素
    return TextEditor.searchLineStyle(el.parentNode as HTMLElement, root, name);
}

至于编辑框里的文本,我写了一个函数,在编辑框失焦以后进行自动「标准化操作」:

  1. 获取编辑框里的 HTML
  2. 解析样式树
  3. 清空编辑框,用从样式树渲染得到的 HTML 替换原始内容

渲染完成的 HTML 为扁平的,意味着不会出现任何嵌套元素。为了避免出现「用户只选中了一个元素里的文本」带来的麻烦,我想了个办法,强迫浏览器每次剪切出的文档片段首层只有元素:把每一个字单独放在一个 <span> 里。以第一个例子的文本为例,最后渲染出的 HTML 是这样的:

<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255); font-weight: 700;">凤</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255); font-weight: 700;">凰</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255); font-weight: 700;">卷</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255);">是</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255);">一</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255);">种</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255); font-style: italic;">奇</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255); font-style: italic;">怪</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255); font-style: italic;">的</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255);">点</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255);">心</span>
<span style="color: rgb(255, 255, 255); text-decoration-color: rgb(255, 255, 255);">。</span>

(实际上各个 <span> 之间并不包含空格或换行,因为行内元素之间的空格/换行在浏览器中会被渲染为一个空格。)

那如果用户只选择了一个字怎么办?对于这种情况,我会读取和操作它的父级元素的样式。

不过,这种情况仅适用于对编辑框内已有文本进行操作的情况。如果用户一直输入文本,没有让编辑框失去焦点,然后选中了一段特定的文本并试图添加/删除样式,编辑器就崩坏了。

对于这种情况,我又补充了一些比较难看的 hack,但是目前并没有在本质上解决这问题。所以这就是为什么我提醒大家输入完文本以后先点一下编辑器外面了(

更新:现在,每当你应用一个样式以后,编辑器就会自动失焦并优化 HTML 结构,所以这种不可靠情况应该是暂且这么解决了。

结论

富文本编辑器是一种很让用户和开发者难过的东西,即使我降低标准,屏蔽除 Minecraft 支持的所有特殊样式,我依然没有把它做到令我完美的程度;至少目前而言,这是让我感到很遗憾的事情了。

这告诉我们:

  • 作为合格的 Minecraft 玩家,熟练背诵样式代码和一大堆常用颜色还是很重要的
  • 还是不要随便碰富文本编辑器坑了

就酱。

's avatar

中秋打油诗一首

故国路漫漫,佳节灯阑珊。

遥望人结伴,俯视影落单。

一碗白米饭,半瓶百岁山。

曲尽人已散,孤苦不堪言。

farseerfc's avatar

【譯】替 swap 辯護:常見的誤解

這篇翻譯自 Chris Down 的博文 In defence of swap: common misconceptions原文的協議CC BY-SA 4.0 ,本文翻譯同樣也使用 CC BY-SA 4.0 。其中加入了一些我自己的理解作爲旁註,所有譯註都在側邊欄中。

翻譯這篇文章是因爲經常看到朋友們(包括有經驗的程序員和 Linux 管理員)對 swap 和 swappiness 有諸多誤解,而這篇文章正好澄清了這些誤解,也講清楚了 Linux 中這兩者的本質。值得一提的是本文討論的 swap 針對 Linux 內核,在別的系統包括 macOS/WinNT 或者 Unix 系統中的交換文件可能有不同一樣的行爲, 需要不同的調優方式。比如在 FreeBSD …

's avatar

在新款 MacBook Pro 以 EFI 的形式安装 Windows 10(2020 更新)

本教程不适用于 ARM Mac!

为什么?

最近我想玩 Minecraft Windows 10 Edition,但是我在学校,我自己的电脑只有一部 MacBook Pro 2017。

那为什么不用 Bootcamp 呢?

因为我也不知道为什么,只有无尽的黑屏与 Windows 10 安装向导初始化的画面。我无法开始安装。

而且 Bootcamp 还有其它的缺点,具体可以看 这里

顺便少数派那个教程有点过时了,我在我的新款 MacBook Pro 安装时撞了些坑,所以决定写个新教程,造福人类。

准备材料

  • Windows 10 ISO
  • UNetbootin
  • 8G 以上的 U 盘(建议使用 USB 3.0 的)
  • USB Hub(建议使用 USB 3.0 的)
  • 支持 Windows 的键鼠套装(在安装完整的 Bootcamp 驱动以前,内置键盘和触摸板用不了的)
  • 机智的你

如果你使用的是带 T2 芯片的新款机型,请先根据 官方说明 允许通过外部 USB 设备启动。

我的安装环境

  • 机型:MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)
  • 操作系统:macOS 10.13.5 (17F77)
  • 磁盘:256GB,只有 macOS 分区

分出 Windows 分区

假如你打算留下 macOS,请使用这个命令调整 macOS 分区大小:

diskutil apfs resizeContainer disk1 200GB    # 200GB 指你给 macOS 留的分区大小

这些数值可以酌情调整。但是,请确保有足够的剩余空间。

制作 Windows 10 安装盘

下载好 Windows 10 ISO,插上你的 U 盘,然后先格式化一下:

我们把 格式 设置为 MS-DOS (FAT)方案 设置为 主引导记录

然后我们打开 UNetbootin,选择 Diskimage,打开我们准备好的 Windows 10 ISO。下面的 Type 当然选择 USB Drive,然后将 Drive 设置成你要制作安装盘的 U 盘(如果不确定的话,可以把主机上所有其它 U 盘和读卡器什么的都拔掉,这样就只有一个选项了)。

然后点击 OK,耐心等待写入完毕就是了。

在写入完毕后,我们打开 启动转换助理,选择 操作 => 下载 Windows 支持软件,并将保存位置设为我们的安装盘的根目录下。接下来我们会需要的。

下载即将结束时会向你请求权限,这是正常的,直接输入密码确认就行了。

如果你尝试过 Boot Camp 安装

如果你没有尝试过 Boot Camp 安装,请跳过这一节!

直接照着 这篇帖子 里面的方法做就行。

以及你需要先把 SIP 暂时关掉。

开始安装

把你的 U 盘、键盘和鼠标插上你的 USB Hub,并连接 MacBook。

重启你的 MacBook 并按住 Option 键,你会看到有好几个磁盘的选项。按方向键选择 黄色图标的 EFI Boot,然后回车。

然后先按提示一路走下去,然后到了选择磁盘这一步,你会发现没有磁盘可选。这是正常的,我们还需要加载驱动程序。

我们点击 加载驱动程序,再点击 浏览,找到 C:\WindowsSupport\$WinPEDriver$\AppleSSD64,确定。然后点击下一步。

稍后,我们就可以看到我们的磁盘分区了。我们按照正常的方法创建好分区,然后继续一路向前就是了。

安装驱动

当我们进入安装好的 Windows 10 以后,找到你安装 U 盘下的 WindowsSupport\BootCamp 文件夹,运行里面的 setup.exe,安装驱动程序,然后重新启动就是了。

然后就大功告成了,你的内置键盘、触摸板等一系列硬件都可以使用了。

(在我设备上的)已知问题

  • Windows 睡眠时间过长以后,你需要经过完整的开机过程才能还原。
  • 每次返回 macOS,你恐怕都需要按住 Option 键手工选择 macOS 分区。在设置里改 启动磁盘 不管用。
  • 蓝牙工作异常
Horo's avatar

如何可能正确的以大部分人可能会接受的方式提出电脑相关的问题

汝问咱为啥标题起得这么长,那是因为咱不敢保证所有的人都乐于接受这种提问方法啊……

以及似乎能衍生到其它领域?那就靠汝自己斟酌了。

本文参考了下面几篇文章的观点,某些文章可能正在经常的被提起。

  • 提问的智慧,原文作者 Eric S. Raymond,以开放源代码运动(不是自由软件运动,这个的领导者是 Richard Stallman)的提出者和主要领导者为人所知的黑客。本指南将教你如何正确的提问以获得你满意的答案。
  • 如何有效的报告 Bug,以程序员的视角阐述如何提交一份足够准确的 Bug 报告
  • X-Y 问题,一种常见的令人疑惑的提问方式,至于为啥令人疑惑嘛……
  • 真的,再这样提问就没人理你了,以提问的智慧的方法提出不一定是电脑相关的问题的方法,大概吧。

提问真的有那么多讲究的地方嘛?

大多数时候如此,因为有一个大前提:

“我们(在很大程度上)是自愿的,从繁忙的生活中抽出时间来解答疑惑,而且时常被提问淹没。所以我们无情的滤掉一些话题,特别是拋弃那些看起来像失败者的家伙,以便更高效的利用时间来回答赢家(winner)的问题。”

大多数人其实都是出于各种志愿目的回答来自不知道何处的提问的,除非……(“我本来想这样拒绝他的,但是他给的钱实在是太多了……”)

所以 …

Jixun's avatar

血污:夜之仪式 – 7 项修改器

测试于 GOG 的 Patch 1.10 (2020.05.07) 版本,Steam 版本可能不支援。在 GOG 购买

《血污:夜之仪式(Bloodstained: Ritual of the Night)》的封面图

修改器项目

CE 表内容

修改功能都做成了开关类型,在左侧的方框里勾上即可启用,取消勾选则还原。

  • HP 相关
    • 一拳超人:双方承受一次伤害后即死(剧情锁血除外)。
    • 无敌:顾名思义,免疫伤害。
  • 蓝条不掉:使用技能的时候,蓝条(MP)不会减少。
  • 商店白嫖:在商店购买的时候不消耗金币。
  • 道具无消耗:使用道具时不消耗道具。
  • 无限跳:未降落时可以一直跳。速降会失效(跳+↓)。
        获得二段跳能力后可以下降的时候也跳。
        部分特殊游戏模式下无效。
  • 调试信息:调试用的一些信息。并没有什么实际用途。
关于一拳超人修改项目(有剧透)

如果需要打真实结局(True Ending)的话,那么在打吉贝鲁(Gebel)的时候需要关闭这一项目。


下载

关于 Cheat Engine 修改器表

※ 你需要安装 Cheat Engine 来开启修改器表文件(.ct)。
  Cheat Engine 是一个开源的游戏作弊软件,请不要将其用于多人对战游戏。


演示动画

击杀路上的小怪 我起了,一枪秒了,有什么好说的(剧透注意)
初见直接秒杀吉贝鲁Gebel,喜提 BE 一枚。
's avatar

为什么大家都在安利这一样东西?

大概无数次我在各大群组里(看见别人)求某种东西的推荐,然后群友们就异口同声的说:「XXX(指同一件东西)不香吗?」

但是这样的安利反而让我感觉不安,因为:

  • 一种东西肯定有它自己的优缺点,但是在那些群友的口中,给人一种 XXX 非常完美的错觉。
    真的,跑分不能说明一切。
  • 安利这种东西的群友中,大概率有相当一部分是它的 fanboy,然后如果你提出不太一样的安利,大概会跟你争个面红耳赤,甚至引起严重的不愉快。
    好吧,我大概是已经屈服于这种「政治正确」了。
  • 有的群友可能只是云用户,在瞎起哄而已。

所以为什么通常情况下,我越来越不想在群聊中为这种事情浪费时间了。我宁可自己问问 Google 娘。

's avatar

Dream Diary - 7,8

#2019

她站在跑道上,望着晚霞点缀着的海面。

我远远地望着她,揣摩着。她到底在看些什么,又在想些什么呢。是把海面看作是浩渺的宇宙,粼粼的波光当作是将抵的舰队?抑或是遥望海的另一端的本土,思念着许久不见的家人……和恋人呢?

再次遇到她,是在来到这艘航母之后的事了。当然我没有大模大样地问她在来这之前是否有恋人,以免打草惊蛇。不过就我的推断,开朗积极如她,赢得一段感情应当是不在话下。

一阵晚风袭来,我不禁打了个寒颤。她不冷么。不过即便如此,她依旧一动不动地站在那里,但不知为何,看起来更加遥远了。

这次,我没有跟她说上话。

2018年,未知外星文明入侵。

2020年,人类灭绝。

#2010

星期六的下午。

老实说,我不是那么讨厌学习,但是我也不是那么喜欢课外班。考试本应该是比较受到同等教育的学生的学习成果的行为,课外班、补习班的存在打破了这一平衡,也打破了学生们投入到素质教育的时间的上限。通过放假时间上课来获取更加优秀的成绩,我认为这是一种恶性竞争。

好事是这种恶性竞争几年后就会被明令禁止。我不禁嫉妒起未来的小弟弟小妹妹们,他们可以傻乎乎地在这烈日之下自由地奔跑嬉戏、中暑晕倒。我摇了摇头,摆脱开这一想法。

10 年后的日子像鬼魂一样,曾时不时在我眼前出现。那段时间,我感觉自己像失去了灵魂的躯壳一样,浑浑噩噩,终日不知所措。对未来一无所知的人们反而成了我羡慕的对象。那种感觉过了一个月才渐渐消退。不如说,我学会了转移焦点。取而代之的是及时行乐的想法,与其忧虑注定到来的末日,还不如珍惜当下,多享受一番。

按照这个逻辑,我现在应该是在室内一边吹着空调,一边打着游戏的。我之所以冒着烈日来参加这个课外班,一个原因是家里没有游戏可以打,另一个原因则是这里有她在。

她——连雪是我的小学同学。她平易近人,虽然有点别扭,容貌——按照小学生的标准——算是不错的,而且成绩向来优秀。在除了成绩,就几乎没有什么好比的小学生眼里,她自然而然就成为了憧憬的对象,还是个小屁孩的我当然也包括在内。我的成绩?现在的话,算是不错啦。

闲话休提。可能是觉得难以接近的原因吧,虽然是同学,小时候的我却没怎么和她有过交集。对此我有那么一丁点,那么一丁点后悔:跟她的关系要是再好一点,就好了。不过对于过去的自己而言只可远观的她,对于现在的我来说,是不是就是容易接近的对象了呢?抱着这样的希望,我想着至少圆一下过去的遗憾,就这样来到了课外班所在的大楼。

这是一栋写字楼,接待处的大姐姐看到我脖子上挂着的名牌,便没有说什么。教室在6楼——我走进电梯,按下按钮。当门将要关闭的时候,我看到另外一个挂着名牌的身影正向电梯这里跑来。是她。我的心跳快了一拍,连忙按下开门按钮。她跑了进来,看到了我,便转向了电梯门,没有说话——。我从面板处退了一步,留出空间。巧也不巧,一个男人在电梯门再次关上之前,也跟了进来。男人按了5楼。

我瞥了她一眼,她像是在沉思,没有注意到我。5楼到了,男人走了出去,她突然也跟着走了出去。我叫住了她。“教室在6楼!”并按住开门键。她张望了一圈,听到我出声喊她,又走了回来。电梯门关上了。“走神了。”她小声说了一句。然后就没有再说话了,像是一副不高兴的样子,可能是因为出丑的缘故吧。


下课后。看着一个人收拾书包的她,我不知从哪里涌上了勇气。我追上了她,问道,“一起回去吗?”

毕竟有一段是顺路,我心里在给自己找借口。

她瞅了我一眼。“随便。”漫不经心地回答道。

我们走在大楼和行道树的阴影之间。热气让我呼吸困难,知了在汽车的呼啸声中肆意聒噪。我莫名地想吃冰淇淋。香草味的大杯装。

“好热啊。”我像是在对自己说。她没有回应,像不觉得热似的。

一个拐弯之后,是一个长长的下坡。路面被太阳晒得发热,路两边稀疏地种着树, 在人行道上洒下零零落落的树荫。路上人影稀疏, 偶有汽车经过。我们一边聊着关于课外班的话题一边下坡。

下了坡,穿过一个小公园。公园里没有人。小公园没有滑梯和秋千,不过有一个沙坑。一座城堡矗立在那里。我下意识地走到沙城堡前。我突然有一种奇怪的感觉:这个沙堡将要融化,像初春的雪一样,变回沙子,落进池里。魔法的时间就要结束了。

连雪走在前面, 我跟在后面。快到她家门口的时候,她停下脚步,转过身来。

“今天过得很愉快。”她说道,脸上露出笑容。不像是平常的她的笑容。

“我也——”

钝器打击的声音。

我的意识就此中断。

17windy's avatar

[8月16日更新P2]17Windy的七日杀A19原创MOD-[近战包]+[远程包] 稳定版

注意:不要光看数值面板,很多武器都有很多数据显示不正确或者没有显示,看小书图标的文本说明!!!

偃月刀用捅的我也觉得很难受,不过七日杀官方就这么几个双手动作,其他的都是砍到1/3就停了,所以用捅的还能稍微好看点,多理解。

更新细节日志:

0816P2 远程包增加了特殊射钉枪:英特纳雄耐尔,特点是手持的时候制作品质+1,单次射击爆发10发铁钉,可通过工作台制造或者高级任务奖励小几率得到;增加了普通版MP7、M9A1的工作台制造配方。

0816 近战包部分属性优化,较为重要的有:太阳舞者不再造成长时间单体火焰伤害,而是有小几率造成群体短时间较高火焰伤害;铁狐现在的火焰伤害强化了;两把智力系近战武器调低了特效触发几率,增加了基础伤害。

0816 远程包部分属性优化,较为重要的有:安迪密恩现在不再造成单体电击伤害,而是有几率造成小范围群体电击;风之力提升了伤害和弹速。

0816 部分汉化文本修改,本次更新仅有xml修改。

0814 修复了拳套武器位置错误的问题(此处实名diss七日杀开发组)

0814 平衡和调整了部分进展/远程武器,让不同武器的差异性变大

0814 完善了部分武器的性能文本说明

0808 近战包新增肉斩骨断(力量+2)、绿色奇迹(对僵尸特攻)、青龙偃月(高截肢)、重生之刃(高潜行倍率)四把近战武器,重制了最后的武士和黑降两把武器的造型

0806 11:00 修复风之力箭矢位置问题@yichen手动狗头

0806 远程包新增 MP7、纯白仓萌、风之力三把远程武器

0806 近战包刀刃类部分武器属性buff

0801 远程包新增一把.44手枪、一把无限弹药的智力系步枪

0801 近战包新增长矛纳迦法、双手锤塔、单手剑永恒魔埚

0729 远程包新增两把霰弹枪:长射程的羞辱(游戏内显示射程不对,实际15)、基地保卫战决战兵器台风排笛(大幅度降低移动速度和体力、增强防御力)

0729 远程包修复几把特殊弹药武器的伤害值过低问题

0729 远程包武器属性调整

0726 近战包数据微调,远程包更新完成

0725 近战包更新完成

's avatar

明天再和朋友相见

在 RSS 中无法显示完整的格式,不同的 RSS 阅读器所显示的内容也可能会有很大差距。为了能够获取到完整的内容,最好还是点开网页链接看。


读着视觉小说[1],是三位高中生一起在社团活动后的傍晚互相道别的场景。其中一人向另外两人开心地说道:“那么明天再和两位相见喔!”然后跑跳着离开了。

刚刚还在吵吵闹闹的场景,在一人离开之后一下子就安静了下来。看着屏幕上笼罩着夜色的背景,正在带入着主角思考的黄鼠,一瞬间感到了一丝孤独。尽管刚刚还在很开心,但不可避免地,一天马上就要结束了,大家都需要各自分道回家了。有趣的活动到现在已经停止,而朋友们也会是在接下来的一段时间里见不到的了。

但黄鼠在下一刻就意识到这是不需要过度感伤的,因为我们还有明天,我们在明天、十几个小时之后就会马上再见了。刚刚也是这样说的,“明天再和两位相见”,不是吗?这不是长时间的道别,所以黄鼠的孤独感转变成了期待——好期待明天的到来呀。只要熬过今晚的几个小时,再睡一觉,在明天就又可以很快和朋友相见,产生更多有趣的经历了。

不过很遗憾的是,这一切都只是在小说里发生的,黄鼠的上述的感受也只是针对那个虚构的故事的。而在现实中,除去有期待少有的特别活动(像是旅行计划)的到来外,似乎已经忘记自己上一次这样期待第二天的日常的到来是什么时候的事情了。

黄鼠印象中的校园日常,几乎就是充满了缺乏活力的轮回。每天早上起来,急急忙忙度过早晨,就开始了不一定很无聊,但大都很累人的课。结束了之后,回到家才可以放松一会,但也不能放松很长时间,因为马上父母就要回来监督自己完成作业了。每一天的自己,整天几乎全部的期待,都放在了放学回家后到父母回家前的那段“自由时间”里,似乎每天都是为那一小段时间而活。在这段时间结束后,下一次能够留给自己的时间就只有完成作业后(或放弃了完成作业后)躺在床上的时间了。而再往后,就又得挨过一整天的学校才能再次享用这份安逸了。

黄鼠不是在学校中没有能够愉快玩耍的朋友,但与朋友的交往,却很少能够像这次在小说中感到的那样,抵消上学的劳累,令黄鼠充满对明天的期待。

现实中的黄鼠,劳累于上学而期待在家的时间,而这段视觉小说中的小小对话,却把两者完全反转过来,开始期待与在学校的朋友们相见,而对在家的时间反而感到孤独了。

不过这只是视觉小说,黄鼠不是主角。而站在读者的角度的话,黄鼠只能对这样的日常表示羡慕了。能够有对每天怀有这样愉快的期待,真好呀。

黄鼠回不到像高中那样的校园生活了,因为在现实中只能向前看。在未来,即使能够找到工作而不是饿死或啃老,所面对的也大概只能是比校园更加乏味且劳累的社畜生活——实习的经历让黄鼠意识到了这一点。每天回家后几乎就像被抽干了体力一样,除了生活必要的事情(像是做饭),唯一想做的事情就是躺在床上耍废,看一些没有营养的 YouTube 视频,刷一些更缺少营养的社交媒体。游戏——甚至是相对低投入的手机游戏——都很少有心思去玩。写兴趣项目和学习更多知识的动力什么的,就更加被鸽子叼走了。在这样的日常中,每天值得期待的东西,就比学生日常更加稀少了。

黄鼠就经常这样想,做社畜的日常好辛苦呀好累呀好可怕呀。所以在读视觉小说时产生了“对明天日常的期待”的心情时,真是被自己惊讶到了呢。原来自己也可以对日常感到开心和期待。但可惜只是针对在小说中存在的虚构日常。

黄鼠看到这么一小段对话,就写了这么一长篇文章,诚实说,也是令自己觉得蛮惊讶的。不过在写完之后,黄鼠并没有什么深的感悟。要说整片文章有一个什么中心结论的话,那就是——二次元真好呀。 🐁


  1. 尽管这并不重要,但如果在好奇的话,黄鼠在读的视觉小说是《魔女的夜宴》。但请不要把这当作是黄鼠的推荐。故事的主要内容并没有和黄鼠在这篇文章中的感悟有很大联系。

除特殊说明以外,本网站原创内容采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。转载时请注明来源 FiveYellowMice 的博客 ,以及原文链接

violet's avatar

Leetcode - Remove Duplicates from Sorted List II

https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

Return the linked list sorted as well.

Example 1:

Input: 1->2->3->3->4->4->5
Output: 1->2->5

Example 2:

Input: 1->1->1->2->3
Output: 2->3

 

violet's avatar

Leetcode - Plus One Linked List

https://leetcode.com/problems/plus-one-linked-list/

Given a non-negative integer represented as non-empty a singly linked list of digits, plus one to the integer.

You may assume the integer do not contain any leading zero, except the number 0 itself.

The digits are stored such that the most significant digit is at the head of the list.

Example :

Input: [1,2,3]
Output: [1,2,4]

 

violet's avatar

Go Routine Practice - tcp server&client

 

violet's avatar

Go Channel Practice V - 祖安萝莉

idea 来自 https://www.zhihu.com/question/402635037/answer/1302122540

逻辑没有照抄,只是做 channel 使用方法练习。

 

violet's avatar

Leetcode - Task Scheduler

https://leetcode.com/problems/task-scheduler/

You are given a char array representing tasks CPU need to do. It contains capital letters A to Z where each letter represents a different task. Tasks could be done without the original order of the array. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle.

However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of time between any two same tasks.

You need to return the least number of units of times that the CPU will take to finish all the given tasks.

 

Example 1:

Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation: 
A -> B -> idle -> A -> B -> idle -> A -> B
There is at least 2 units of time between any two same tasks.

Example 2:

Input: tasks = ["A","A","A","B","B","B"], n = 0
Output: 6
Explanation: On this case any permutation of size 6 would work since n = 0.
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
And so on.

Example 3:

Input: tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
Output: 16
Explanation: 
One possible solution is
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> idle -> idle -> A -> idle -> idle -> A

 

Constraints:

  • The number of tasks is in the range [1, 10000].
  • The integer n is in the range [0, 100].

 

violet's avatar

C memory layout

High Addresses ---> .----------------------.
                    |      Environment     |
                    |----------------------|
                    |                      |   Functions and variable are declared
                    |         STACK        |   on the stack.
base pointer ->     | - - - - - - - - - - -|
                    |           |          |
                    |           v          |
                    :                      :
                    .                      .   The stack grows down into unused space
                    .         Empty        .   while the heap grows up. 
                    .                      .
                    .                      .   (other memory maps do occur here, such 
                    .                      .    as dynamic libraries, and different memory
                    :                      :    allocate)
                    |           ^          |
                    |           |          |
 brk point ->       | - - - - - - - - - - -|   Dynamic memory is declared on the heap
                    |          HEAP        |
                    |                      |
                    |----------------------|
                    |          BSS         |   Uninitialized data (BSS)
                    |----------------------|   
                    |          Data        |   Initialized data (DS)
                    |----------------------|
                    |          Text        |   Binary code
Low Addresses ----> '----------------------'
Fenking's avatar

杂谈

在今天提交完最后一门课计组的report之后,我似乎意识到这是我自从大学以来第一个看上去可以无所事事的暑假。其实大概也不能,但是交完report,与学校暂时无瓜葛之后,涌上心头的第一感觉确实疲惫,困乏,无聊,无奈。这是今年在日本第二个什么都干不了的假期。距离上一次写博客时隔两年了。我也说不清为什么不写博客,似乎人在日本,就慢慢身心都远离了我所熟悉的事物,慢慢就变得蹒跚了起来。

作为杂谈,简单聊聊最近几年在干什么吧。
上一次博文是在2018年暑假,那就从2018暑假左右开始谈起。
2018年4月,高考失败后去日本再战。
2018年暑假,在国内复习与准备各种大学面试笔试。
2018年9月-2019年3月:我的住址从东京墨田区两国搬到东京墨田区向岛,再到千叶船桥市东船桥。
第二次留考仿佛高考的复写,考的稀烂,一塌糊涂,我靠着前期合格的立命馆大学一路冲刺其他学校,可惜全败。最后在4月开学前做了一趟全国环游,拜访了不少同学,但同时大概也是我最后一次和同学近距离相处。
2019年4月-2019年8月,在立命馆的第一个学期,仿佛被丢进大山里的城市人,手无寸铁,好不适应。为了提前解决这种问题,去考摩托驾照。
2019年9月提前回到日本,想着要早一秒拿到驾照。最后10月拿到了,这一年的浪人计划(重考)也开始了。10月开始的后半学期我没报太多课,空出来很多时间去继续战留考,战校内考。这次的留考拿到了凑合的成绩,后期的校内考充斥了整个假期,只得在元旦匆匆回国安稳一周。虽然在国内也是每天泡图书馆。现在我怎么也想不到,如果元旦不回国,我甚至会被困在日本一年以上。
2020寒假。一直埋在家里复习。碍于疫情,1月开始就在日本自我隔离,除了2月底出门在本周岛东西横断三次连续4天疯狂校内考以及零碎不连着的其他校内考,以及三月中旬骑摩托环琵琶湖一圈以外。没离开过南草津一步。
网课大二如期而至。每周12篇report冲破了最后的心理防线,半年以上和两个以下的人现实性往来,本质上被国内几乎所有过去的人遗忘,逐渐变成了连说话都会觉得奇怪的家伙。今天貌似又一次放假了。按理说今年不能再浪学校,却是要准备大三编入转学考试的,9月的托业还在等待我,但我貌似已经没有精力去做。未来去哪里读研?如何快速回国?貌似都是想不明白的问题。

放假即破灭。
没想到所谓放假的第一天,就发现老本行被掀锅,过去所想价值瞬间不存在了。
恍恍惚惚撞到一个介绍国内东方社群/同人圈的b站专栏,内容之详细,以至于和我多年慢慢积累的大量早期国内东方资料基本重合类似,甚至追加了渔场之前漂亮同人堂的内容–这部分对我而言一直是可有可无,本身算不上东方圈的开始的。但它积攒的却是鱼总Fish的思念,以及更早一批人的本心–其实也对我不是特别重要的部分,因为从渔场跟随到漂亮,最终会扯到kid–这之后还会牵扯更多同人团体,而我只对东方感兴趣。
文章本身几乎无可挑剔,国内东方社群历史本身了解的途径就很窄,一旦找到了就只有那一条路–大部分人可能都是从同一个地方出发,最后达到同一个目标,仿佛可以穿越回过去的历史,一笔一划写的清楚,容不下其他言语。虽然其中有些部分是怎么找到的,我也一直抱有迷惑,但文章本身其实是无所谓的。令人灰心的,是他的个人网站。
和很多之前认识的同龄人类似,这也是个年龄相仿的神童,一个早早就在互联网上可以留下自己一笔的人物,以至于他们的丰富,导致他们对丰富的否定。网站上写道,文章的决定是十分坎坷的,但它的过程,确实一气呵成的,平均一周一篇。我不清楚我之外的人对东方同人了解甚多甚少,不知道他们是否有高强的起点,至少我得到很多消息,除了通过那条固定的渠线,还有很多迷津小路,但大概都是被在一周之内完成了。
心态恢复平静,继续看对方的网站上的文章。
东方在对于国内东方社群,或者就是东方同人社群,大概就是爱好者从深受影响而同人创作,进而走进专业化,原创化,最后创造新的project,成为领域的或东或西某一角。不论是从同人音乐走向专业电音领域的haku,还是从同人画手走向制作人,新的project人的海猫氢弹库,更或者早已在各个领域扎根的各路神仙,国内的东方社群虽然早就断代了,在渔场死后,在小镇死后,但是某些时代的终点就是某些时代的起点,宛如一所大学,终将毕业之人最后携手或者不约而同成立新的时代,逝去的中国东方同人史一直在诉说这一轮回,不同于贴吧开始的新东方社群。这些话在多年前就深根蒂固于心,乃至一直是我作为东方同人史爱好者砥砺前行的唯一动力–回到过去,追忆过去,透彻过去,【历史可以被遗忘,但不能被遗失。】【中国东方同人史就是从引导一拨人,从同人走向专业,走向毕业,走向新的project。】没有被展示出来的后句,我甚至不敢在任何地方明示。不过同样的话已经被说过了,现在再高呼出来,也不过是巧妙的蹭罢了。就像看到论文出版而痛悔不已的科学家,自己做过的一切仿佛都是尘埃,自己接下来做的一切仿佛都是追随。我曾在好几年前就对【想法】的保护烦恼许久–世界的残酷容不下共同的荣誉,时间的存在更是让每个随着时间跳动的生命对它致以崇高的敬意,先与后是绝对的,不容打破,不能打破,只能在懊悔中为自己感到羞愧。然而像这样写文章去发泄自己,终究不过是在暗地里为自己的嫉妒发生,往如在病床上看着电视里的范马父子,大喊【这我也做得到呀!】的杰克范马一样。
终究还是早了半年。而这半年是我没有接触它的半年,这半年的结束也一如平常,在现在结束的一瞬间,它就追上了你。
从今往后,我似乎也失去了在这一领域探索的义务,失去了一切的动力。自讨苦吃,接下来的每一步看似在证明,其实在挽救,在诡辩,在狡辩。
暂时确实不知道该怎么做。
看着对方的文章,感觉自己也是这么想的,但为什么你会说出来而我没有找到说出来的机会。但仔细想想自己仿佛又不是这样想的,对自己的本心产生了疑惑,一篇文章竟让我如此动摇。仿佛中国东方同人史的研究,早已成为自己最后的遮羞布,需要完成的却没有完成。
仔细想想所处的环境,说不定环境确实早就了这样的我–缺少交流,缺少实感,往如在新东方上培训课的竞职者,失去了所有爱好的土壤。
仿佛一直在说日本是宅的圣地,也是东方爱好者的圣地,是东方的土壤。我为何对这片土地毫无发觉,毫无触觉呢?盈月纪年和境界生命物语在交付完成到zun先生手上的那一瞬间开始,对我而言zun先生不过是zun而已,在我心中它仿佛不会对我而言所爱着的东方产生任何价值,其实自己爱着的东方也并不一定是zun先生自己创造的价值。时代,人群,年岁,机遇,信息,身份,是多元的遭遇创造了早期的中国东方同人圈(社群),而又是这多元的每一层每一丝的紊乱错综,一步步让它走向自我灭亡。我总会在最后发现我到底想说什么。我不想谈东方爱好者,但我必须谈。那个时代的东方爱好者可能并不存在什么转型,他不论之后去做什么,不做什么,他都永远剥不掉身上【早期东方同人圈一员】这样的标签,这个标签也在无时无刻不影响这个人。也许算一种“因果”,但我希望这层神秘的面纱不会脱落。

我不想当追随者,但也不得不当。
2020.7.23

violet's avatar

Leetcode - Top K Frequent Elements

https://leetcode.com/problems/top-k-frequent-elements/

Given a non-empty array of integers, return the k most frequent elements.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

Input: nums = [1], k = 1
Output: [1]

 

依云's avatar

Linux 的环境变量怎么设

本文来自依云's Blog,转载请注明。

最近,Arch Linux 的 pam 包将更新到 1.4.0,然后因为一个字符的变化,不少中文用户都开始避难了:pam_env 将默认不读取用户的环境变量设置。许多中文用户使用~/.pam_environment文件来配置 fcitx 输入法所需要的那三个环境变量。更新之后,这些配置将不再生效,意味着他们可能无法输入中文了,于是大家热烈讨论现在要在哪里配置环境变量比较好。

当然了,在 pam 的配置文件里加上user_readenv=1就可以恢复原先的行为。只是,pam 的开发者这么改当然是有原因的:CVE-2010-4708。简单地说,就是在鉴权模块里设置用户指定的环境变量不安全,怕被提权。

2020年08月20日更新:Arch Linux 打包时添加了以上这个配置

当然啦,桌面用户完全可以去改系统级的环境变量设置,只要你愿意让你个人的配置信息跑到系统配置那边去的话。

正文

用户经常需要设置一些环境变量,比如输入法,比如语言和地区选项,比如自定义的 PATH,等等。然而 Linux 的环境变量又不像 Windows 那样有一个专门设置所谓「系统环境变量」的地方,而是所有环境变量全部由子进程继承父进程获得,这么一路继承下来的。于是,想要给所有想设置上的进程都设置上,就不是一件简单的事情了。

这里关注的是桌面用户,自然首先从图形界面说起。

使用 X11 的桌面环境,通常通过 display manager 来登录,比如 lightdm 和 sddm。这俩都支持 ~/.xprofile。这个文件会在启动过程中被 source,使用的 shell 是由 dm 自己确定的。lightdm 和 sddm 都是用的 /bin/sh(分别位于 /etc/lightdm/Xsession 和 /usr/share/sddm/scripts/Xsession 文件里)。可以看到,除了读取 .xprofile 外,lightdm 也会读取 .profile。sddm 甚至连 bash、zsh、tcsh、fish 的启动配置脚本都给读了。

对于 Wayland,我这儿只有 sddm,类似的,也是各种 shell 的都给你读了。(当然就不读 X11 相关的东西了。)

而对于手动 startx 的用户,~/.xinitrc 里自己写上呗。还有 VNC 啥的,也都可以自己看启动脚本找到方法。

没有图形界面的登录,通常是从 tty 或者 ssh 登录。这两者都是登录到命令行 shell。写在 shell 配置里就可以了。具体到 zsh,我是写到 ~/.zprofile,这样不管是否交互,只要是登录 shell 就执行,不登录的大概已经继承到了。另外有些情况可能写这儿也没有用,比如 cron 调用的时候。非要搞的话其实可以写到 ~/.zshenv,这个基本上所有的 zsh 都会读,只能被系统级设置或者命令行参数禁用掉。而如果是 bash 用户,.bash_profile .bash_login .profile 这仨,会读找到的第一个(sddm 也会按这个顺序找,但是 lightdm 只会找 .profile)。Arch Linux 现在默认会给新用户建立 .bash_profile,我建议 bash 用户把它改名成 .profile。这里有个图 ,大致上说明了 bash 和 zsh 不同情况下会读取的配置文件

systemd 用户实例也有一份环境变量,用于通过 systemd 启动的用户级服务,通过 systemctl --user 的子命令可以查看和更新。D-Bus 也有一份,可以通过 dbus-update-activation-environment 命令来更新。这俩通常不需要用户操心,桌面环境应该会维护好。

如果你用 tmux 的话,那么 tmux 还有一份,用于新创建的窗格。tmux 会在被连接时更新 update-environment 选项指定的环境变量。当然你也可以用 tmux setenv 子命令去更新。这个尤其值得注意,因为 tmux 可能在不同的环境中被连接,导致环境变量混乱(比如 X11 下没有 DISPLAY 变量)。

要注意的一点是,不同的登录过程并不是只会读取自己专属的配置文件,尽管大家都在努力,也并没有完全统一好用的配置文件。所以有些环境变量,你可能想尽量避免重复设置(比如冗长重复的 PATH 项就很讨厌)。好在这些都是 shell 脚本,不光能设置环境变量,也能做条件判断。

要查看当前进程的环境变量,可以用 env 命令。要查看别的进程的,去读它的 /proc/PID/environ 文件,或者开个 htop 然后在进程上按 e 键。要看看某个环境变量在不同进程里是什么情况,可以用我写的小工具 compare-env

Linux 软件生态的特点就是这样,没有谁规定一定要用怎样的方式做一件事情,所以大家的想法总会有些出入,就导致设置个环境变量还有这么一大群配置文件(systemd 还有一份呢,我还没读没试所以没说)。但另一方面,理解它们也是十分容易的:不用猜测,不用撞大运般地耗费时间去尝试,顺着代码摸过去,总能弄明白软件的行事逻辑。即使文档像 pam_env 那样前后矛盾,也有源代码这条路可以精确刻画软件的行为。

violet's avatar

React - 组合 vs 继承

React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用。

Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。

如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。

 

violet's avatar

React - 状态提升

在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。
虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。
如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。

 

violet's avatar

React - 表单

在 react 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState() 来更新。

React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。

可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项
<select multiple={true} value={['B', 'C']}>

当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作

在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value,但输入仍可编辑,则可能是你意外地将value 设置为 undefined 或 null。

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 Formik 是不错的选择。

's avatar

Something Incredible Between Them

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.


Mathew feels troubled today. It’s not because of the mock final exam that has just passed. Although indeed he didn’t do well, it doesn’t bother him too much. He believes that studying is easy, relatively speaking. All he have got to do is to read those textbooks, remember these bullet points, do things that have all been successfully done by billions of people. Even he has trouble understanding, he has literally all the resources to help him. As long as he studies seriously, his grades are bound to rise to a respectable level. That’s what he believes, at least.

What troubles him, is the tiny screen glowing in his hand. On the top of the screen is the logo of a popular social media. On a more prominent part of the screen is a name that can drive Mathew’s heart up and down in milliseconds, Remi. The post on screen is not sent by Remi however, it’s another person that occupies a large part of Mathew’s mind, Karter. The post reads: I just witnessed something incredible of Remi.

Someone asks “What’s incredible?” and judging from the response Karter has given, it’s apparently very secretive.

“It’s always been like that.” Mathew mumbles. “Saying some prompt as if wanting to tell us more of a story, but just as the prompt provoked others’ curiosity, they tell us no more.”

Well, that’s what couples do, isn’t that right? They share things on social media not for others to see, but for themselves to have the pleasure of showing off and being showed off. Mathew doesn’t think it’s a badly selfish thing. In fact, he likes it when people announce their love, and enjoys reading romantic stories, both real and fictional. In fact, he craves for it. It doesn’t only make him happy for those in love, but also gives him a space for illusions as if he is part of it. This is usually the case with people that Mathew is unfamiliar with, and especially the case when the couple in question includes the one he admires.

Indeed, “admires” is the most Mathew dares to say about his feeling towards Remi.

Mathew met Remi when he was a first-year high school student. Remi was in the second year. In a school-organised activity, where 3-5 students from both first-year and second-year collaborated to complete a collage on a topic freely chosen. The groups were randomly arranged, although students had a chance to rearrange if they were unhappy about their groups. Mathew, a couple of unimportant people, and Remi were in the same group, they were quite happy about the arrangement, so it stuck. Mathew quickly took a liking on Remi. Remi is interesting, artistic, caring, and knows quite a lot. Remi didn’t seem to think Mathew as a nuisance either, so they exchanged contacts and chatted regularly. The collage didn’t end up being a hit, but it was good enough that both their parents praised them for it.

Later on, Matthew loved hanging out with a bunch of second-year students after each class, including Remi. The first-years and second-years were in the same building, so it wasn’t a rare sight in this high school. To others, they just looked every other students hanging out together, but to Mathew, the times they spend together were incredible. The more Mathew knew about Remi, the greater he thinks about Remi. And Mathew thought, this was going to last forever.

But obviously it couldn’t. A year passed by, Remi had gone up to the third year. The third-years were in a different building, “In order to better assist their studies.” The time Remi could spend with Mathew had drastically decreased, their interaction was then mostly restricted to social media and weekends. It was also around the start of the academic year that Karter started to appear in Remi’s social media posts.

Mathew didn’t care that he didn’t know Karter personally. “It’s just someone Remi thinks is cool.” Mathew thought. He knew, by reputation, that Karter was a prominent member in the school soccer team, but then resigned for some reason. “Whatever, it doesn’t bother me at all.” Mathew told himself.

But then Karter was mentioned more and more by Remi. They posted more and more photos of them together. They also put each other’s username in their social media profile. “Wow, they must be really good friends.” Mathew thought, “Karter must be a genuinely interesting person. I kinda want to know more about.” Mathew wasn’t jealous or anything, at least he knew he shouldn’t be.

One day, Mathew got a 2-ticket bundle at a local movie theatre, it could only be used when 2 people went at the same time. So Mathew invited Remi.

“Is it just us two?”

“Yes, it’s a 2-ticket bundle.”

“I guess…​ but Karter probably wouldn’t be happy about it.”

The rest of the conversation didn’t matter. The last sentence Remi said stuck in Mathew’s head. “Why would Karter be unhappy about Remi and I going to a movie?” Mathew pondered, “What kind of people would be unhappy? Is it…​ what I imagined it is?”

There wasn’t a sure answer, but later observations only confirmed Mathew’s guess. Remi and Karter, are in a relationship. They didn’t announce it when it started, but they are, now.

It is when Mathew made this guess that he also realized how jealous he was over having someone that holds an exclusive lock on Remi. There are now things that Remi must keep Mathew away from. There are now things that Remi cannot do for Mathew.

It was time that Mathew needs to accept the new reality, one that has Remi occupied by someone Mathew wasn’t close with.

This reality has been largely accepted by Mathew today. He knows he can only watch Remi and Karter from a friendly distance. He has also attempted to get close with Karter, but they don’t much in common. Mathew knows that there will always be a large part of Remi he can’t acquire forcibly, so the best course of action, is instead watch for things that Karter and Remi shared about their life publicly, consuming that, relieves his insatiable hunger for more Remi.

But Mathew also knows that he can’t do this forever. In matter of months, Remi and Karter, both as third-years, are graduating. Whether they’re going to the same college is unkown to Mathew. Obviously he could just ask Remi. But he is too scared to ask, as he is with many more questions to Remi. The same goes for many things Mathew wants to say to Karter and Remi, but he will never say them, as they are, at most, irrespective and irresponsible. Mathew thinks it’s best for him to befriend Karter, and be friends with both Remi and Karter for as long as they can keep in contact.

Yet, Mathew is feeling troubled by something incredible between them that he can’t know about. 🐁


Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

RecursiveG's avatar

MikroTik RB4011 访客网络配置备忘

前言


由于之前陆陆续续添置了不少电子设备,以及更换 ISP 的原因,机架上连了5台设备,每台各负责一点点事情,不管是配置还是调试都很麻烦。再加上旧路由器不能很好同时处理千兆 NAT 和 VLAN,于是最近入手了一台 RB4011iGS+5HacQ2HnD-IN,把这一堆乱七八糟的设备统统换掉。主要需求有三点:

  1. 划分2个 VLAN,一个内部网络,一个访客网络。
  2. IPv4 和 IPv6 双栈接入。
  3. 因为路由器直接暴露在 Internet 上了,所以防火墙一定要配好,包括 VLAN 之间的访问也是靠防火墙来控制的。
SgDylan's avatar

DIY 显示器亮度自动调节

DIY 显示器亮度自动调节记录存档。

AlisterTT's avatar

板烧的LOGO

给板烧画了个水印用的LOGO,感觉有内味了,中文字体思源黑,英文字体Helvetica Black,感觉有内味儿了。

AlisterTT's avatar

吾有猫,其名为“板烧”

今年决定养只猫了,找到了一家靠谱的猫舍,店长每天都在勤勤恳恳的做着直播,目前观众还不是很多。

2020年6月5日,猫舍的小母猫铁扇公主生下了四只小猫,一只梵,一只蓝,板烧排行老三,以及排行老四、最可爱的蓝白妹妹。

板烧是四兄妹中最小的一只,但是很努力的在喝奶,这两天打开直播看到他,心里都在默默的说:“板烧,冲鸭!”

因为体型最小,所以也最努力喝奶的板烧

至于他为什么要叫板烧呢,开始想着如果选到了妹妹,就叫“茶茶”,选到了弟弟,就叫“信长”或者“秀吉”,可是朋友都觉得这俩名字太正经,于是探讨了大半天无果…后来小豆子建议选我最喜欢吃的食物,那当然是暴风雪或者麦旋风了,用来起名就欠了点,灵光一闪“板烧”这个名字就出来了,甚至已经做好了设定,等小朋友长大了就叫“大阪烧”(误)…

2020年6月12日晚上的板烧

小猫在妈妈身边要待六十天左右,估计八月份就能接回家了,板烧,一定要健康呀!!

farseerfc's avatar

系統中的大多數文件有多大?

你覺得,你的系統中大多數文件大概有多大?

這是一個很有意思的問題,你可以試着先猜一下。

基於對系統中保存文件的瞭解,可能有這樣的思考過程:

  • 我收藏了好多照片,每個有 2~5MiB 吧。
  • 我下載了好多漫畫,每個 100KiB 左右,這些大概佔了不少比例。
  • 我還收藏了不少動畫電影電視劇,雖然這些文件總數量可能不多?
  • 我下載了 Linux 的源碼,那裏面每個 C 代碼文件都幾千行,每行 100 字寬,平均也得有 30KiB 吧,有幾萬個源碼文件呢,佔比應該挺大的……

問題中「大多數」其實是個挺不精確的稱呼,換個精確點的問法:你覺得你的系統中 文件大小的中位數 大概在什麼範圍內?或者說,文件系統中 文件大小的分佈情況 一般是怎樣的曲線?

這個問題其實還有多種別的問法,比如:一個常見的桌面或者服務器系統中,多大的文件算大文件, 多小的文件算小文件,什麼範圍內的大小算是普通呢?

經歷過基本的科學教育的人 …

依云's avatar

终端色彩总结

本文来自依云's Blog,转载请注明。

终端转义序列里有一个 SGR 代码用于表示「粗体或者增加强度」的字体渲染。相当多的终端将其实现作更明亮的颜色而非粗体字,也有很多终端应用程序如此(比如 mutt 中的 brightXXX 颜色使用此代码;新一点的 mutt 支持使用 lightXXX 来表示亮色了)。

然而最近,GNOME 终端决定将这个「或」字去掉,让 SGR 代码 1 表示粗体,亮色使用另外的颜色代码来表示。这无疑导致了我看到的许多软件里出现粗体字。有时候看着还行(比如 systemd、htop 中的),有时候会让人觉得很不舒服(比如 zsh 里打着命令,字体一会儿变粗一会儿变普通粗细)。我就认真去查了一下相关信息,作出相应的配置更改,顺便做个总结笔记好了。

转义序列

「选择图形展现」(Select Graphic Rendition; SGR)序列用于设置字体的渲染方式。其格式为CSI (code ";")* code m。CSI 就是 ESC "["。一个序列里可以给出多个代码,使用分号分隔。如果一个都不给出,当作代码 0 解释。

八色

这是最基础的八种颜色,代码 30-37 设置前景色,40-47 设置背景色。

16色

这就是大多数终端设置里,那个14色的调色板所指定的色彩了。为什么只有14色呢?因为默认的前景色和背景色是单独的设置项。

如序言中所说,相当多的终端将代码 1 解释为亮色,也就是上述八色再加上对应的明亮版本。

自 GNOME 终端此次改革之后,代码 1 只管粗体。要亮色,使用代码 90-97 设置前景色,100-107 设置背景色。除了 GNOME 终端外,也有一些其它终端跟进,比如 alacritty。有些终端保留了将代码 1 解释为亮色的选项,而 GNOME 终端最近去掉了这个选项(但是还有一个将粗体字显示为亮色的选项)。

256色

16色怎么够?于是出现了256色。

ESC[38;5;<n>m用于设置前景色,ESC[48;5;<n>m用于设置背景色。其中,n 是一个数,0-7 同代码 30-37,是那最初八种颜色。8-15 是它们的亮色变种。16-231 是216种彩色,在RGB空间构成了一个6x6x6的立方体。232-255 是24个灰阶色彩。

一些标准中,这中间的分隔符使用冒号。

真彩色

现代显示器普遍使用8位值表示RGB中每一个通道、共计24位了,终端也不能落后呀。于是有些终端也支持了这个被称为「真彩色」的24位色彩了。

代码格式是256色的扩充。ESC[38;2;<r>;<g>;<b>m来设置前景色,ESC[48;2;<r>;<g>;<b>m设置背景色。这里的 r、g、b 当然就是RGB色彩空间的值了,每一项的取值范围是 0-255。

测试脚本

整合了我从各处收集到的展示方式,写了一个脚本,可以直观地感受终端对各种色彩的支持情况:

colors.py

参考资料

SgDylan's avatar

使用 VMAF 对静态图片进行质量评估

使用 VMAF 评估图片质量,测量算法性能。

SgDylan's avatar

DIY 显示器背景灯 Lightpack

最近自己弄了一套显示器背景光,在此稍微记录一下。

Jixun's avatar

移除游戏添加的美式英文键盘

很多 FPS 游戏(如 CS:GO)会在启动的时候添加一个美式英文键盘,但不会在结束游戏的时候卸载这个键盘。

在 Steam 社区找到一个批处理的解决方案,不过需要保存两个额外的 xml 文件在硬盘来调用。

研究了下相关的 API,用汇编做了个简单的小程序。

下载

GitHub | 历史版本

源码使用 BSD 3-Clause 协议授权,可以在 JixunMoe/RemoveUSKeyboardLayout 获取。

启动游戏并移除美式键盘

  • 此处使用的例子是“军团要塞2”。
  • 其他游戏的 Steam APPID 可以从其对应的商店地址找到。

将批处理与编译后的文件放在同一目录:

REM 启动游戏 start steam://rungameid/440 REM 等待游戏启动并添加键盘 timeout /t 5 REM 移除键盘 %~dp0\RemoveUSKeyboardLayout.exe
Code language: DOS .bat (dos)

或,使用 VBScript 脚本启动来隐藏命令行窗口:

Set objShell = CreateObject("Shell.Application") Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.GetFile(Wscript.ScriptFullName) strFolder = objFSO.GetParentFolderName(objFile) ' 启动游戏 objShell.ShellExecute "steam://rungameid/440" ' 等待游戏启动并添加键盘 WScript.Sleep(5000) ' 移除美式键盘 objShell.ShellExecute strFolder & "\RemoveUSKeyboardLayout.exe"
Code language: VBScript (vbscript)
Jixun's avatar

FC 魂斗罗1 汉化

FC 上的《魂斗罗1》汉化是我在 2016 年做的,很多内容已经找不到了唔…

汉化说明

人生第一个汉化的 FC 游戏。因为字数不算太多,就试着汉化了下。

汉化基于有剧情、有动画的日文版本制作。

汉化完后,发现以前汉化 FC 游戏的前辈真不容易啊~

汉化内容参考自英文翻译版、百科、贴吧以及网络上的各种资源,因为我看不懂游戏里的日文…

标题界面调色有点小问题,脸有点黑… 不要在意细节~

以上。

  • 标题画面
  • 剧情画面
  • 过场画面
FC《魂斗罗 1》汉化截图

下载

更新说明

  • v2 – 2016.08.04
    • 修复地图界面的卡顿 & 乱码。
    • 翻译了之前漏翻的隐藏信息。
    • 加强了断句信息的停顿。
  • v1- 2016.08.03 – 初版发布

碎碎念

汉化的时候折腾了好一段时间;因为网络上文章的丢失(很多做 FC 汉化的前辈的部落格都无法正常访问了),很多时候都是自己一个人在瞎折腾。还好最终给我折腾出来了

汉化日记可以在旧博客上找到(含目录),日期大概横穿了从立项到发布的日期哈哈。

对代码部分所做的代码更改则使用 BSD-3-Clause 授权协议,可以在 JixunMoe/ContraNES1TranslationPatch 获取。

过段时间把汉化日记补充道后续页面吧。

Jixun's avatar

QMC 解密

腾讯旗下的《QQ 音乐》所使用的音乐文件加密格式就是 QMC,本质上就是将整个文件读入后用 XOR 处理。

下载

GitHub | 历史版本

源码使用 MIT 协议授权,可以在 JixunMoe/qmc-decode 获取。

使用说明

使用解码程序对 .qmc 文件进行处理。

打开命令行,执行像上图一样的命令即可。

.\qmc_crypto.win64.exe .\周杰伦-青花瓷.qmcflac .\周杰伦-青花瓷.flac

具体调用方法:qmc_crypto.win64.exe "<qmc文件路径>" "<解密后文件路径>"

更新历史

  • v1.0.1 – 2019.07.16 – 修正 Linux 下无法编译的错误,感谢 @nswa-project 提交的 PR。
  • v1.0 – 2019.05.25 – 初版发布。

碎碎念

解密代码其实是从某个第三方 APK 里面提取的,里面有一个叫com.tencent.qqmusic.business.musicdownload.vipdownload.PayProcessor 的类。

这个类里面有两个函数,分别是 native_decryptnative_encrypt

估计就是从手机版《QQ 音乐》里面提取出来的了吧?

Jixun's avatar

100% 橙汁 – 资源解包工具 v1.1

100% 橙汁(100% Orange Juice)是一个类似大富翁的游戏。

支持类型

  • .dat 后缀文件解密
  • 伪装为 ZIP 压缩包,内部包含后缀名为 .dat 的加密文件(如 animation.pak
  • 将合并后的 ogg 拆分成单独的文件(如 bgm.pak
  • 将合并后的 wav 拆分成单独的文件(如 se.pak

使用方法

OrangeDecryptor <输入文件> <输出路径/目录> [模式]
Code language: Bash (bash)

如果不填写模式的话,将尝试自动识别。

下载

季寻储存 | GitHub | 历史版本

源码使用 BSD 3-Clause 协议授权,可以在 JixunMoe/OrangeDecryptor 获取。

更新记录

  • v1.1 – 2018.01.18 – 修正 XP 下的支援
  • v1.0 – 2018.01.?? – 最初版本发布

碎碎念

本来想顺便把源码放 GitHub 的,可惜用 Everything 一搜才发现找不到源码… 看来是某次重装的时候不小心删掉了呀。

在去年的一个系统备份里找到了

参考来源

这里的参考来源全凭记忆了,毕竟这东西写了有两年了。

  1. WAV 文件格式的说明:
    https://wiki.fileformat.com/audio/wav/ (存档)
  2. OGG 文件格式的说明:
    https://xiph.org/ogg/doc/framing.html (存档)
Jixun's avatar

蓝光原盘压硬字幕并跳过前 10 秒

找到个未加密的原盘,里面有一堆字幕轨,发现第一轨(0:s:0)是需要的字幕。

字幕的前几秒是一些观影说明,也可以一同去掉(大约 10 秒长度)

蓝光里的音频倒是有一堆,直接用第一个音轨(0:a:0)就行。

目标播放设备不支援 x265 也不支援 10 bit,因此编码器选择的是 x264 以及 Profile 为 main@4.0

跟着网上找的说明,加上一些 B帧、引用帧的设定以及一些调整,最终的压制脚本如下:

#!/bin/sh set -ex INPUT_PATH="bluray:/path/to/disk" OUTPUT_PATH="/tmp/Film.name.YYYY.1080p-ENCODE.mp4" META_TITLE="中文名 - 外文名 (年份)" AUDIO_MAP="0:a:0" nice -n 15 -- \ ffmpeg -stats -i "${INPUT_PATH}" \ -filter_complex "[0:v:0][0:s:0]overlay=0:0:enable='gte(t,10)'" \ -metadata title="${META_TITLE}" \ -movflags +faststart \ -c:v libx264 \ -crf 20 \ -flags +loop \ -deblock 0:0 \ -bf 16 -b_strategy 2 -refs 6 -maxrate 12M \ -profile:v main -level 4.0 \ -preset slow -tune animation \ -c:a libfdk_aac -vbr 5 \ -map "0:v:0" -map "$AUDIO_MAP" \ "$OUTPUT_PATH"
Code language: Bash (bash)

如果是 Windows 环境,可能使用 AviSynth 脚本会比较好一些?调滤镜什么的也方便。

最后压出来的视频大概 2 个小时长,因为 B 帧 设定得比较大,每次快进都是 5 秒一跳;不过考虑到视频是拿来本地收藏用的,还是能够接受。

参考资料

  1. 介绍 ffmpeg 中对应的 x264 选项的文档:
    https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
  2. 对 ffmpeg 中使用 -deblockalpha 参数却不能使用的解决方案:
    https://stackoverflow.com/a/23539599
  3. ffmpeg 百科对滤镜的教程:
    https://trac.ffmpeg.org/wiki/FilteringGuide
  4. ffmpeg 的滤镜文档:
    https://ffmpeg.org/ffmpeg-filters.html
Jixun's avatar

仓库用度盘投稿助手

虽然说当初是为了某个仓库而做的辅助插件,没想到现在因为百度网盘的严打而再次热门起来 _(:3__

脚本安装

首先需要安装一个脚本管理器,推荐使用 暴力猴,然后安装脚本兼容版)。

推荐使用原版,兼容版做了一些转译操作来兼容旧内核浏览器而增加了体积。

源码使用 MIT 协议授权,可以在 JixunMoe/dupan-helper 获取。

脚本说明

安装脚本后,百度网盘的管理页面会多出一些功能:

  1. 自定义分享(提取密码)
  2. 批量重命名
  3. 标准提取码(秒传链接),分享再也不会失效啦 ^1
  • 批量重命名选中的文件
  • 自定义分享密码
  • 导入标准提取码

^1 取决于百度网盘是否针对文件特征码进行屏蔽。

支持的提取码

  • 「游侠」的「肚娘提取码」。 游侠格式的提取码一次只能添加一个,不能与其他格式混搭。
  • 梦姬标准提取码
  • PanDownload 的 bdpan:// 协议链接。

※ PanDownload 的作者于二〇二〇年四月被捕,请小心目前在网上流传的版本。

标准提取码生成器

标准提取码生成器以及相关说明请参见从「百度网盘标准提取码生成器」。

via these people and places