Compare commits

..

5 Commits

Author SHA1 Message Date
Yue-bin
0c012c8747 docs(plan): 更新通信通道划分逻辑
conv的划分现在精确到单人单channel级别,
增加了目标channel的获取步骤,并调整了计算逻辑。
2026-04-18 06:21:30 +08:00
Yue-bin
f090fa8eb1 feat(kcpable): 添加协议常量和Hello消息类型
新增Const类定义版本常量,创建Hello消息类用于网络握手,
并添加MsgType枚举定义Hello消息类型为75
2026-04-18 06:19:35 +08:00
Yue-bin
55510cf46c feat(Protocol): 添加ConvGenerator类用于生成KCP连接的会话标识
新增ConvGenerator类,用于根据本地和远程SteamID以及通道号生成唯一的会话标识符(conv)。
该实现使用位运算确保不同连接间的conv唯一性,支持多通道通信场景。
2026-04-18 06:18:39 +08:00
Yue-bin
14afa59833 docs(plan): 添加conv协商机制说明
在plan.md中新增了conv协商的技术方案,详细描述了如何通过两端steamid计算出唯一的conv值
2026-04-18 06:17:55 +08:00
Yue-bin
0d483a2bd9 docs: 添加Kcpable项目计划文档 2026-04-18 04:05:22 +08:00
5 changed files with 110 additions and 0 deletions

6
Protocol/Const.cs Normal file
View File

@@ -0,0 +1,6 @@
namespace stick.plugins.kcpable.Protocol;
public static class Const
{
public const byte version = 1;
}

13
Protocol/ConvGenerator.cs Normal file
View File

@@ -0,0 +1,13 @@
using System;
using Steamworks;
namespace stick.plugins.kcpable.Protocol;
public class ConvGenerator(CSteamID local, CSteamID remote, int channel)
{
readonly ulong smaller = Math.Min(local.m_SteamID, remote.m_SteamID);
readonly ulong bigger = Math.Max(local.m_SteamID, remote.m_SteamID);
readonly int channel = channel;
private ulong Combined => smaller ^ (bigger << 1) ^ ((ulong)channel << 2); // 位交错
public uint Generate() => (uint)(Combined & 0xFFFFFFFF) ^ (uint)(Combined >> 32); // 全部64位异或压缩到32位的conv
}

13
Protocol/Hello.cs Normal file
View File

@@ -0,0 +1,13 @@
using Lidgren.Network;
namespace stick.plugins.kcpable.Protocol;
// 在懒得发的原有网络栈上握手使用的Hello消息
public class Hello
{
public byte version = Const.version;
public void Send(P2PPackageHandler p2pHandler, NetConnection user)
{
p2pHandler.SendSocketP2PPacketToUser(user, [version], (P2PPackageHandler.MsgType)MsgType.Hello);
}
}

9
Protocol/MsgType.cs Normal file
View File

@@ -0,0 +1,9 @@
namespace stick.plugins.kcpable.Protocol;
// 这里是在懒得发的原有网络栈上发消息使用的msgtype
// 0-38 被懒得发用了
// 目前应该暂时只需要一个 Hello ,其它都走 kcp 内部了
public enum MsgType : byte
{
Hello = 75, // 'K'
}

69
plan.md Normal file
View File

@@ -0,0 +1,69 @@
# Kcpable
本项目旨在改善柴的联机延迟
*本项目灵感来源于[`streamable`](https://mcp-docs.cn/specification/2025-11-25/basic/transports#streamable-http)*
## 优势
### 降低延迟
使用[`kcp`](https://github.com/skywind3000/kcp)覆盖在`steamworks``udp`栈上,用少量冗余带宽换取更低的延迟
### 向下兼容
本插件会采用类似`streamable`的方式进行握手,这是与使用`tcp`栈发包的游戏原始行为并行的,若握手失败则什么都不做,非常透明
## 协议
本协议与项目同名,称为`Kcpable`,重点在`-able`,旨在简单可靠地完成协议升级,同时保持对不支持协议的客户端的透明性(~~其实没有那么透明,但是懒得发帮我们把识别不出来的包丢掉了~~
存在本地和对端两个游戏客户端在p2p的背景下两者是等价的都有如下两种状态
- 支持`kcp`协议
- 不支持`kcp`协议
考虑到后者大部分情况下为原版游戏/原版网络栈,我们无法对其进行修改,因此后者对于任何原游戏协议外的数据包的处理方式可以看做直接丢弃,所以场景简化为:
- 本地客户端
- 支持`kcp`协议
- 对端客户端
- 支持`kcp`协议
- 不支持`kcp`协议
显而易见的,本地应当主动发起握手,但是考虑到本项目的使用场景与`streamable`还不一样是P2P的因此决定简化为
- 主动行为(发送)
- 进入房间时向所有人发送请求协议升级的握手包~~(考虑重复三次以规避高丢包场景)~~
*还是算了因为默认的tcp本来就会保证送达改成超时重传最多重传三次吧*
- 房间里有新人进入时亦然
- 同时增加发送握手包快捷键以主动向所有人发送握手包加上频率限制防止手动flood
- 被动行为(接收)
- 若在收到合法握手包:
- 若与来源客户端正使用`tcp`连接则对其启用`kcp`连接
- 若与来源客户端已使用`kcp`连接,则无视
- 其余均无视,走游戏默认行为
也就是只发送类似`hello`的包,但是不使用`ack`,因为可以默认收到了合法`hello`就是支持协议
目前暂时不考虑协议降级
### conv协商
直到看到了`kcp`库的文档我才知道conv是需要协商的他相当于一个uuid不过我觉得可以通过约定预协商
conv的划分应该精确到单人单channel
1. 取两边steamid
2. 按大小分成bigger和smaller
3. 取需要发送到的目标channel懒得发只定义了两个
4. 计算
``` csharp
private ulong Combined => smaller ^ (bigger << 1) ^ ((ulong)channel << 2); // 位交错
public uint Generate() => (uint)(Combined & 0xFFFFFFFF) ^ (uint)(Combined >> 32); // 全部64位异或压缩到32位的conv
```
## 其它
考虑在建立了`kcp`连接的玩家名后添加紫色`[K]`以标识