浅谈绘版黑科技

前言

最近受到许多人的启发,决定《稍 微 浅 谈》一下绘版黑科技(大雾)。

这里仍提出警告:文章可能有点长,谨慎阅读。

正文

脚本篇

这里将讲述如何写出跟 LHQing 一样牛的脚本,称霸绘板。

搭建本地测试环境

玩绘版需要有个服务端,而我们不可能花时间自己写一个,所以采用现有的后端 Yur Paintboard 来进行本地测试。

后端建议在 Linux 环境下运行。若您使用的是 Windows,可以在 WSL 上面进行构建。

广告:不知道怎么配置 WSL?这篇文章将教你如何玩转 WSL 配置。

该后端的构建需要 Rust。按照说明编译完毕后运行 ./target/release/setup && ./target/release/yur-paintboard 可以看到绘版成功运行。

至此本地测试环境正式搭建完成!开始进入脚本编写工作。

绘版脚本基础

我们现有的大多数绘版使用 HTTP request(以 Luogu Paintboard 为代表)或者 Websocket(以 Yur Paintboard 为代表),当然我推荐使用 Websocket 协议。

Websocket 协议的网址以 ws 开头,若其经过 TLS 认证应使用 wss,例如:ws://127.0.0.1:2895/wswss://paint.yurzhang.com/ws

要有脚本首先得选好语言。推荐使用 Python、dotNET、Java 或 Python。 这里以 Python 为例。

Python 的基础 Websocket 库 websocket-client 是一个蛮不错的对低级 API 提供访问的库,这里我们使用这个库来编写脚本。

安装 pip3 install websocket-client,注意代码中库的名字叫 websocket

首先建立连接发送验证:

import websocket
websocket.enableTrace(True)

headers = {
    "Content-Type": "application/json",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
}

token = []

def on_open(ws: websocket.WebSocketApp):
    global token
    l = [255]
    for i in range(len(token)):
        sl = token[i]
        if sl == '\n':
            break
            
        l.append(ord(sl))
            
    print("Send auth.")
    ws.send(bytes(l), websocket.ABNF.OPCODE_BINARY)

if __name__ == "__main__":
    ws = websocket.WebSocketApp('ws://127.0.0.1:2895/ws', 
        on_open = on_open)
        
    ws.run_forever()

确保本地服务端正常运行且可以被访问。运行,发现成功连接。

以 Yur Paintboard 为后端时,通过发 0xf9 来爬取整个绘板。

当然,我们知道带宽会炸:真彩色 24bit 的大小为 1000 * 600 的绘板大小为 1.8 MB,但是服务器带宽仅有 3~10 Mbps。

于是 Yur Paintboard 使用了 ZSTD 压缩,可以压至 800 KB。

绘板脚本应实现的功能

绘板脚本的最基本目的是不以手绘方式使现在绘板变成人想要的目标绘板。

称目标绘板与现在绘板上颜色不一样的点为错点。

基础部分

最简单的技术了,目前 Zipoard 官方脚本 YPZ 已经实现。

配置文件

包括图片配置和 Token 列表。推荐采用 JSON。

多 Token

实现方法自然是多线程。当然 CPython 的 GIL 使得效率不那么高,可以试试用 Pypy 运行。

首先编写 Painter 类,内部建立连接。

为了节省带宽,不妨分两类 Painter:主 Painter 既爬绘板又绘制,副 Painter 只绘制。

为了防止高并发干掉连接,须隔一定时间再开下一个 Worker,大概设置为 0.1s。

由于第一个 Painter 第一个连接,规定其为主 Painter。

多图

操作极其简单,先设置一个错点的队列,每个线程绘制时都取队头。

整一个专门扫描错点的线程。因为错点扫描需要爬现在绘板比对,所以该线程需在主 Painter 启动后运行。

先读取图片配置(包括起始点在绘板上的位置),然后遍历每一张图片的每一个点,计算位置并比对,是错点便将其坐标、目标颜色等信息压入队尾。

有了 GIL 后多线程要资源锁什么的都不去管他啦。

还有随机撒点绘制功能,是 EERP(增强人眼识别绘制)的超级弱化版本,只需要 rand.shuffle 一下就行()

自动重连

操作极其简单,开一个布尔数组记录各个 Token 的连接状态。

若一个 Painter 断连了,直接摧毁其创立的线程和其本身。然后标记为断开。

整一个专门重连的线程,定时扫描各个 Token 的连接状态,尝试直接再创造一个 Painter.

注意重启主 Painter 还要重开爬绘板的线程。(我写的时候因为这个调了好久,无语)

随机撒点攻击

攻击性功能(喜),操作也极其简单,就是排除自己的绘制区域然后随机一个点涂上一个随机颜色。

可以采用空暇时间启用的方式,也可以采用分配一定比例的 token 用于随机撒点攻击的方式。

不过还是提醒一下:谨慎使用,别撒到资本家了,小心被直接灭掉。

Pygame 实时战况

注:加了这个会变慢,刘仓去掉了这个功能,但是为了看起来酷一点 YPZ 还是保留了。建议刷新 cd 不小于绘制 cd 的二分之一,或者干脆 1Hz 得了。

由于绘板会下发更新的点,所以我们只需在爬取现在绘板的地方更新显示即可。(这是动态更新)

还有一种固定帧率的,死循环定时扫描,直接重绘一遍也可以,不过会大大降低速度。目前 YPZ 采用了这个方法(快乐码老师你就不能脑袋转个弯吗怎么这个都不优化, 额其实是我懒得改)。

然后还可以做一个可视化分析,比如标记绘板上的热点区域、冷门区域等。

日志输出

日志文件输出:直接新建个文件命名为 log_<startsec>.log, 然后输出日志即可。

然后日志内容主要是记录绘制操作,Token 断开、重连信息等。

进阶部分

一开始还好后来逐渐离谱的部分,但是有些蛮有建设性的,大概前人没玩过。

日志过滤

顾名思义,就是过滤一下日志,以防止一些无用输出覆盖了你想要的信息。

以及在你没有搞出命令框之前给命令系统一个存活空间。

这里把日志分为三类:成功(SUC)、信息(INFO)以及错误(ERR);每一类根据其重要性又有不同。

成功日志包括:Token 验证成功、图片绘制完成、全部图片绘制完成等。

信息日志包括:等待绘制点队列为空、Token 自动重连信息等。

错误日志包括:依赖未满足、模块加载失败、Token 验证错误、Websocket 异常等。

指令系统

一个对于选项实时更改实现的方法(除了用 GUI 控制)。

然后这个东西为了提供一个现代一点的实现,建议使用 curses 库实现一个 TUI,不然容易被日志系统占用终端。

建议实现下列选项:

绘制相关:

  • 绘制模式选择

  • 重载图片

  • 重连 Token

日志过滤相关:

  • 日志过滤等级自定义

  • 日志过滤模式设定

Pygame 实时战况相关:

  • 刷新率设置

  • 是否显示实时战况

  • 是否显示战况可视化

Chunk 攻击算法

当一个资本家企图称霸绘板时,他就需要学会使用聪明的方法打击散户。

显示绘制进度等小功能

图片进度:还是用 curses 库进行绘制。

Token 健康状况:简单计数器,这里不赘述。

效率:计算方式:

\[\eta = \dfrac{\text{实际完成 px 数}}{理论完成 px 数} \times 100 \% \]

图片绘制进度自平衡

当一个资本家有多至 20+ Token 用于维护自己的图片时,这个资本家需要学会动态分配 Token 以平衡进度以抵抗 广大人们群众 散户们的攻击。

现在我们可以选择使用自平衡算法来完成。其实现方式是引入一个平衡权重。

当这张图片的绘制效率低下,考虑逐步增加其平衡权重,当其绘制效率稳定在一个阈值以上,则可以逐步降低其平衡权重。

重构软件 - 模块化
EERP - 增强人眼识别绘制

高级部分

很扯的功能,仅仅是瞎想的,不太可能会实现。

AI-assisted EERP - 人工智能辅助的 EERP
AntiATK - 精确识别反击系统
QPD - 快速摧毁图片算法
posted @ 2023-03-17 22:10  JackMerryYoung  阅读(88)  评论(0编辑  收藏  举报