辅助功能

Background

Recently, I started learning English again. When I listened to podcasts on my iPhone, I found that I couldn’t hear what they were saying clearly, so I used live captions to help me understand what they were saying.

Usage

  1. enable(setting -> accessibility -> live captions)
    alt text

  2. effect image
    alt text

Extra Tips

I suggest you set long tap to toggle the switch.(setting -> accessibility -> touch -> assistiveTouch -> Long Press -> Live Captions)
alt text

JDownloader

Today I want to share a tool to buddys, JDownloader
below is it’s functionalitis

download video from website p

魔方-层先法笔记

参考百度百科

因为是自己的笔记, 所以我只层先法上的部份步骤(多拧几遍,能实现别的步骤)

备注

U(U’)代表把顶层顺(逆)时针旋转90°,U2代表把顶层旋转180°。R(R’)代表把右面顺(逆)时针旋转90°,R2代表把右面旋转180°。L(L’)代表把左面顺(逆)时针旋转90°,L2代表把左面旋转180°。F(F’)代表把前面顺(逆)时针旋转90°,F2代表把前面旋转180°。u(u’)代表把上两层顺(逆)时针旋转90°,u2代表把上两层旋转180°。x(x’)代表把整个魔方以R的方向顺(逆)时针旋转90°,x2代表把整个魔方以R的方向旋转180°。y(y’)代表把整个魔方以U的方向顺(逆)时针旋转90°,y2代表把整个魔方以U的方向旋转180°。z(z’)代表整个魔方以F的方向顺(逆)时针旋转90°,z2代表把整个魔方以F的旋转180°。

具体步骤

  1. 第一层
    1. 第一层的十字
    2. 第一层的四个角

效果如图
第一层

  1. 第二层-两个公式
    情况4:U’F’UFURU’R’
    情况5:URU’R’U’F’UF
    第二层

  2. 第三层

    1. 做好顶面十字

      情况3:FRUR’U’F’
      情况2:FRUR’U’F’ * 2
      情况1:FRUR’U’F’ * 3
      alt text

    2. 顶角归位

    3. 调整顶棱位置

Python面试记录

某一次

  1. Python 的 initnew 的区别

  2. Python 的内存重用机制

  3. Python 的协程是什么

  4. Python 的基本类型有哪些

  5. Python 的上下文是怎么实现的

  6. WSGI/ASGI 是什么

  7. MySQL 的索引原理是什么

  8. MySQL 的四种隔离级别

  9. MySQL 的事务原理是什么
    Atomicity(原子性):事务是一个不可分割的操作单元,要么全部成功,要么全部失败。
    Consistency(一致性):事务执行后,数据库从一个一致状态转换到另一个一致状态。
    Isolation(隔离性):多个事务并发执行时,彼此隔离,互不干扰。
    Durability(持久性):事务提交后,对数据库的修改是永久性的,即使系统崩溃也不会丢失。

某一次

  1. MySQL 的关键字的执行顺序是什么

    1. FROM 和 JOIN
      首先确定查询的数据来源,包括表和连接操作。
    2. WHERE
      过滤符合条件的行。
    3. GROUP BY
      按指定列分组。
    4. HAVING
      对分组后的结果进行过滤。
    5. SELECT
      选择要返回的列,并可以应用聚合函数或表达式。
    6. ORDER BY
      对结果进行排序。
    7. LIMIT 和 OFFSET
      限制返回的行数或跳过指定行。
  2. /login 符合RESTful 规范嘛

  3. Oauth 的四种模式
    隐式
    密码
    客户端
    授权码

  4. Kafka 和 RabbitMQ 的区别

某一次

  1. Python 的异步原理
  2. Python 的GC, Java 的GC
  3. 怎么查询中位数
  4. ExecuteService 的处理请求是 maxPoolSize 和 queue 逻辑
  5. 反向传播的原理
  6. 装饰器的原理
  7. 倒排索引的原理
  8. MongoDB 的 _id解释

某一次

  1. 生成器和迭代器的区别
  2. 三大范式是什么

某一次

  1. 什么时候用到线程,协程, 进程
  2. MongoDB 如果有一张大表3000w行,怎么添加索引

某一次

  1. Redis 的缓存击穿, 缓存雪崩,缓存穿透
  2. MySQL 的查询过程
  3. fastApi 的生命周期
  4. 怎么排查线上的CPU满了, 或者内存满了的情况
  5. Redis 和MySQL 的数据一致性问题

某一次

  1. Kafka 怎么实现顺序的消息队列
  2. RabbitMQ 怎么实现高可用
  3. Doris 为什么性能那么高
  4. 多线程的核心数一般怎么设置

某一次

  1. Kafka怎么是实现消息被多个消费者同时消费
  2. Python 怎么实现动态的 [] 和 .

某一次 周二 2025.4.15 14:00

  1. 输入[1,2,3,4,5,8,10,11,12,13,14] 和 12, 怎么输出所有的2元素组合,如 [1,11], [2,10]
  2. 什么是RESTful
  3. 什么是http的无状态
  4. cicd 有哪些步骤
  5. 用过哪些设计模式

某一次 周四 2025.4.17 10:00

  1. CICD和jenkins

某一次 2025.4.17 15:00

纯英语, 所以记录一下单词

  1. IO intensive
  2. CPU intensive
  3. Mandarin
  4. SQL 和 NoSQL的区别

某一次 2025.4.17 17:00

  1. 有用过IAM吗
  2. 怎么防止存储服务被攻击

某一次 2025.4.17 19:00

某一次 周五 2025.4.18 10:00

某一次 2025.4.18 13:00

  1. LCS 怎么实现
  2. 开闭原则

rsync笔记

背景

之前尝试在openwrt上上启动anything, 实际体验是会把设置直接弄崩,现象是ssh都连不进去,所以也没法debug, 只能手动同步了, ssh 又不支持增量传,所以使用了rsync

1
rsync -avz -e ssh /mnt/share-media/ pi:/mnt/share-media
1
rsync -avz -e ssh /www/luci-static/argon/background/ pi:/www/luci-static/argon/background/

2024在powershell中给桌面端软件签名

背景

使用Electron 打的包直接安装会有提醒, 网上很多方式是使用cmd 的, 我补充一下powershell的
reason

解决步骤

  1. 在服务商买 代码签名证书, 注意不是常见的 SSL证书

这里我先使用自签名证书了

设置环境变量

1
2
3
4
5
6
7
8
9
# 密码
$certPassword = ConvertTo-SecureString -String "123456" -Force -AsPlainText
# 文件
$fileToSign= "D:\niumag\niumagfiber\NiumagSeed-linux-4.1.3-beta.610-x64.exe"
# 执行文件入口
$signtoolPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe"
# 时间服务器
$timestampServer = "http://timestamp.digicert.com"

创建根证书并导出

1
2
3
4
# 创建
$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=AmanoCert" -KeyUsage DigitalSignature -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation "Cert:\CurrentUser\My"
# 导出
Export-PfxCertificate -Cert $cert -FilePath "AmanoCert4.pfx" -Password $certPassword

签名

1
& "$signtoolPath" sign /debug -f AmanoCert4.pfx  -p 123456 /fd SHA256 /tr $timestampServer /td SHA256 -a $fileToSign

效果

签名效果

alt text

属性效果

alt text

重装机兵重构4-功能篇

上一篇重装机兵重构2-地图篇的后续

Day24

  1. 感谢 Jax’s Studio 的无私奉献, 复用背包系统
    inventory

Day25

  1. 背包系统是使用 UGUI实现的, 利用RenderTexture 渲染在 UI Document里面

Day26

  1. RenderTexture 之后的背包不能点击, 使用UGUI重新画了一个tab, 然后把背包重新放到 UGUI里面

Day27

  1. 开源的背包物品交换有bug,直接购买了 Ultimate Inventory System, 竟然还赠送商店系统, 准备直接站在巨人肩膀上

Day28/29/30

  1. 研究Ultimate Inventory System

MySQL表锁为什么不会出现死锁

截止 2025-02-20, 大语言模型还有进步空间哈

我的回答

首先固定一下上下文,不讨论表锁和行锁形成的死锁, 只讨论表锁与表锁之间。
目前的答案是不会形成死锁,原因官方已经说了 解锁
简单的说就是lock表A并想lock表B的时候会隐式的触发表A的解锁,所以不会存在死锁, 如果想同时锁住A和B, 需要这样

1
LOCK TABLES job WRITE, user WRITE;

快照

证明1 X表确实时互斥的

  1. 创建User 和Job 表, 每个表有 id 和name 字段
    效果

  2. 锁住 user 并查询锁的情况
    会话1

  3. 给 user 表 增加列 age, 发现存在锁竞争, 这个DDL pending 住了
    会话2

  4. 解锁 user 表, 发现age 修改好了
    会话1

会话2

结果

证明2 隐式解锁

  1. 锁住 user
    会话1

  2. 给user 增加 gender, 发现卡住
    会话2

  3. 锁住 job 表
    会话1

  4. 发现 user 的改动也结束了, 说明user表确实解锁了
    会话2

效果

补充一下大模型的回答

DeepSeek 回答的不正确

alt text

OpenAI 通过引导可以回答正确

alt text

重装机兵重构3-剧情篇

上一篇重装机兵重构2-地图篇的后续

Day16

  1. 认识一些像素人物的生成工具;
  2. 增加持久化的能力,基于json 的文件的 SaveSystem的实现

Day17

  1. 认识 Unity 的 Timeline
  2. 认识 Timeline 的 Playable Track, Signal Track

Day18

  1. 利用 ProcessFrame 实现 Timeline 中两个 playableAsset 的暂停,等待用户输入才继续播放

  2. 认识 Timeline 的 Animation Track,实现人物移动 + 动画播放
    效果图

  3. 修复 PlayableDirector.Pause 会导致人物会回到初始地点的bug:

    1
    2
    //director.Pause();
    director.playableGraph.GetRootPlayable(0).Pause();

Day19

  1. 处理TextMeshPro的中文乱码:
    从windows-> TextMeshPro -> Font Asset Creator 创建没有效果
    从*.ttf -> creat ->TextMeshPro ->Font Asset -> SPF 这样可以
  2. 简单过场黑屏动画 天亮了 使用 UI -> Panel + UI -> TextMeshPro 实现
  3. 封装打字机,过场动画 天亮了 也可以使用

Day20

  1. 解决Visual Studio 的中文在Unity 乱码的问题:
    在VS里面安装 扩展 -> 管理扩展, 搜索Force UTF-8(No BOM) 2022
  2. 增加逻辑, 开始游戏 -> 三选一角色 + 用户名称 -> 存档
  3. 增加逻辑, 记载游戏 -> 选择存档 -> 进入游戏

Day21

  1. 实现gameObject 在 UI Document 的展示(通过将GameObject 渲染到 RenderTexture)

  2. 实现Player的prefab, 根据职业选择渲染不同的人物

Day22

  1. 根据玩家选择的职业, 动态更新Timeline中animation track的人物的gameobject, 以及更新animation track 中的 animation clip

    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
    private void UpdateAnimationTrack(PlayableDirector director)
    {
    var playerObject = GameObject.FindWithTag("Player");
    Animator playerAnimator = playerObject.GetComponentInChildren<Animator>();
    Debug.Log("playerAnimator is " + playerAnimator);
    foreach (var track in director.playableAsset.outputs)
    {
    if (track.streamName == "PlayerPosition" || track.streamName == "PlayerAnimation")
    {
    if (track.streamName == "PlayerAnimation")
    {
    UpdateAnimationClip(track, playerAnimator);
    }
    director.SetGenericBinding(track.sourceObject, playerAnimator);
    }
    }
    }
    private void UpdateAnimationClip(PlayableBinding track, Animator animator)
    {
    if (track.sourceObject is AnimationTrack animationTrack)
    {
    // 获取该 Track 上的所有绑定(这里是 Animation Clip)
    foreach (var clip in animationTrack.GetClips())
    {
    // 替换现有的 Animation Clip
    foreach (var newClip in animator.runtimeAnimatorController.animationClips)
    {
    if (clip.displayName == newClip.name)
    {
    clip.asset = newClip; // 将新的 Animation Clip 赋值给 Track
    break;
    }
    }
    }
    }

    }

  2. 增加postTimeline 脚本, 实现剧情中人物的销毁和常规人物的位置重置

  3. 在camera上面增加audio source, *.m4a 需要转成 *.wav

    1
    ffmpeg -i input.m4a output.wav

Day23

  1. 打字机支持加速(左键跳过)
  2. 利用UI Docuemnt 实现tab
    tabs

Day24

  1. 实现menu 的隐藏出现,menu里面包含退出,实现游戏的开始->退出->开始的闭环:
    注意UIDocument 的隐藏不要使用SetActive(false), 这样会导致按钮的事件丢失

至此, 素材篇基本结束了,后面进入功能篇

重装机兵重构2-地图篇

上一篇重装机兵重构的后续

Day12

  1. 认识Aseprite, 一个Animated Sprite Editor & Pixel Art Tool。这个工具可以快速把真实的画转成像素风
  2. 认识Tiled, 比Unity自带的Palette 多了一个地形集,画地图更方便
  3. 认识SuperTiled2Unity 可以把Tiled 的地图放入unity里面
  4. 把多个Aseprite 合并成一个
    1
    2
    3
    4
    C:\Users\amanoooo\Desktop\Aseprite\aseprite --batch building-2.aseprite building3.aseprite building4.aseprite building5.aseprite building6.aseprite building7.aseprite building8.aseprite --sheet buildings.png


    C:\Users\amanoooo\Desktop\Aseprite\aseprite --batch cliff1.aseprite cliff2.aseprite cliff3.aseprite garbge.aseprite grass.aseprite grass2.aseprite hedge.aseprite hedge2.aseprite hedge-left.aseprite shizijia.aseprite stone.aseprite tree1.aseprite water1.aseprite sand.aseprite 三个点.aseprite 宝箱1.aseprite 地板1.aseprite 地板2.aseprite 地板3.aseprite 工作台.aseprite 平台.aseprite 油桶1.aseprite 油桶2.aseprite --sheet envs.png```

Day13

  1. 在Tiled 上面增加对象层,增加对象, 然后unity 使用脚本实现把对象改成IsTrigger ,实现门碰撞逻辑, 通过SuperCustomProperties 来获取门的名称, 实现不同门的跳转. 脚本如下

DoorManager 放在空对象上

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
using UnityEngine;
using SuperTiled2Unity;

public class DoorManager : MonoBehaviour
{
void Start()
{
// 获取所有的 SuperObject 组件

// 使用 FindObjectsByType 替代 FindObjectsOfType
SuperObject[] superObjects = FindObjectsByType<SuperObject>(FindObjectsSortMode.None);


foreach (SuperObject superObject in superObjects)
{
// 检查对象是否有碰撞器
Collider2D collider = superObject.GetComponent<Collider2D>();
if (collider != null)
{
// 动态添加 TriggerDoorNamePrinter 脚本
superObject.gameObject.AddComponent<DoorHandler>();
}
}
}
}

DoorHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using UnityEngine;
using SuperTiled2Unity;

[RequireComponent(typeof(Collider2D))] // 确保对象有 Collider2D 组件
public class DoorHandler : MonoBehaviour
{
private SuperCustomProperties customProperties;

void Start()
{
// 获取 SuperCustomProperties 组件
customProperties = GetComponent<SuperCustomProperties>();

// 确保碰撞器是触发器
Collider2D collider = GetComponent<Collider2D>();
if (collider != null)
{
collider.isTrigger = true;
}

// 打印 doorName 的值
PrintDoorName();
}

void OnTriggerEnter2D(Collider2D other)
{
// 当触发器被触发时打印 doorName
PrintDoorName();
}

private void PrintDoorName()
{
if (customProperties != null)
{
// 检查是否有 doorName 属性
if (customProperties.TryGetCustomProperty("doorName", out CustomProperty property))
{
Debug.Log($"Door Name: {property.m_Value}", this);
}
else
{
Debug.LogWarning("No doorName property found on this object.", this);
}
}
else
{
Debug.LogWarning("No SuperCustomProperties component found on this object.", this);
}
}
}

Day14

  1. 实现不同场景人物的初始化位置:
    在Awake 里面查找自定属性 IsEntry
  2. fix迭代更新tmx对应的tsx对应的png(补充素材),出现的问题是unity的地图素材乱了,和tiled的不一致,通过更新tsx的高度修正bug

Day15

  1. 认识Tiled editor 增加类
  2. 实现多场景多入口的切换:
    进入IsDoor 的时候, 检查DoorName(场景名) ,检查 DoorIndex, 然后切换到对应的 Scene,查找EntryIndex==DoorIndex的 IsEntry 的gameObject
  3. 临时处理人物位移 x+0.5, y-0.5

Day16

  1. 处理bug:
    老版本的MetalMax有个逻辑是,场景A->B通过楼梯, B->A也是通过楼梯, 要求是到B的初始化状态是在楼梯上, 这时候按照我们的写法, B会因为OnTriggerEnter2D 立即切换到A, 解决方案是在 IsEntry 上面也增加一个Trigger, 只有 OnTriggerExit2D 才能是IsDoor 的trigger启用
    代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    using UnityEngine;
    using SuperTiled2Unity;
    using static UnityEngine.EventSystems.EventTrigger;

    [RequireComponent(typeof(Collider2D))] // 确保对象有 Collider2D 组件
    public class DoorHandler : MonoBehaviour
    {
    private SuperCustomProperties customProperties;
    private bool disableByEntry = false;


    void Awake()
    {
    // 获取 SuperCustomProperties 组件
    customProperties = GetComponent<SuperCustomProperties>();

    var isDoor = IsDoor();
    if (IsDoor())
    {
    // 确保碰撞器是触发器
    Collider2D collider = GetComponent<Collider2D>();
    if (collider != null)
    {
    collider.isTrigger = true;
    }
    }

    // 检查是否是入口门
    if (IsEntry())
    {

    Collider2D collider = GetComponent<Collider2D>();
    if (!isDoor)
    {
    collider.enabled = false;
    }

    if (GetEntryIndex() == SceneLoader.Instance.DoorIndex)
    {
    if (isDoor)
    {
    disableByEntry = true;
    }
    MovePlayerToThisDoor();
    }
    }
    }

    private void Update()
    {
    }


    void OnTriggerEnter2D(Collider2D other)
    {
    if (!disableByEntry)
    {
    SceneLoader.Instance.SwitchScene($"mm/scenes/{GetDoorName()}");
    } else
    {
    Debug.Log("ignore scene loader due to disableByEntry: " + disableByEntry);
    }
    }
    void OnTriggerExit2D(Collider2D other)
    {
    disableByEntry = false;
    }

    private int GetDoorIndex()
    {
    if (customProperties.TryGetCustomProperty("DoorIndex", out CustomProperty DoorIndex))
    {
    if (DoorIndex != null)
    {
    return DoorIndex.m_Value.ToInt();
    }
    }
    return 0;
    }

    private int GetEntryIndex()
    {
    if (customProperties.TryGetCustomProperty("EntryIndex", out CustomProperty Index))
    {
    if (Index != null)
    {
    return Index.m_Value.ToInt();
    }
    }
    return 0;
    }

    private string GetDoorName()
    {
    if (customProperties != null)
    {
    // 检查是否有 doorName 属性
    if (customProperties.TryGetCustomProperty("DoorName", out CustomProperty DoorName))
    {
    var DoorIndex = GetDoorIndex();
    Debug.Log($"Door Name: {DoorName.m_Value} Index:{DoorIndex}", this);
    SceneLoader.Instance.DoorIndex = DoorIndex;
    return DoorName.m_Value;
    }
    else
    {
    Debug.LogWarning("No doorName property found on this object.", this);
    return null;
    }
    }
    else
    {
    Debug.LogWarning("No SuperCustomProperties component found on this object.", this);
    return null;
    }
    }

    private bool IsEntry()
    {
    if (customProperties != null)
    {
    // 检查是否有 IsEntry 属性
    if (customProperties.TryGetCustomProperty("IsEntry", out CustomProperty property))
    {
    return property.m_Value == "true";
    }
    }
    return false;
    }

    private bool IsDoor()
    {
    if (customProperties != null)
    {
    // 检查是否有 IsEntry 属性
    if (customProperties.TryGetCustomProperty("IsDoor", out CustomProperty property))
    {
    return property.m_Value == "true";
    }
    }
    return false;
    }

    private void MovePlayerToThisDoor()
    {
    // 查找玩家对象
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    Debug.Log($"player is {player}");
    if (player != null)
    {
    Vector3 alignedPosition = new Vector3(
    transform.position.x + 0.5f,
    transform.position.y - 0.5f,
    transform.position.z);
    // 将玩家移动到门的位置
    player.transform.position = alignedPosition;
    }
    else
    {
    Debug.LogWarning("Player not found in the scene.", this);
    }
    }
    }

效果

至此, 素材篇基本结束了,后面进入剧情篇