├── gidra ├── __init__.py ├── plugins │ ├── windseed_blocker.py │ ├── change_account.py │ ├── checksum_bypass.py │ ├── change_nickname.py │ ├── seed_exchange.py │ └── commands.py ├── proxy │ ├── handshake.py │ ├── packet.py │ ├── kcp_socket.py │ └── __init__.py ├── reader.py ├── mhycrypt │ ├── __init__.py │ └── mt64.py └── __main__.py ├── python-kcp ├── lkcp │ ├── __init__.py │ ├── compat.h │ ├── kcp.py │ ├── core.pyx │ ├── ikcp.h │ └── ikcp.c ├── .gitignore ├── requirements.txt ├── .gitattributes ├── setup.sh ├── setup.py ├── README.md ├── test │ ├── utils.py │ ├── latencysm.py │ └── testkcp.py └── LICENSE ├── keys ├── MHYSignCN.pem ├── MHYSignOS.pem ├── MHYPrivCN.pem ├── MHYPrivOS.pem └── SigningKey.pem ├── pyproject.toml ├── .gitignore ├── poetry.lock └── LICENSE /gidra/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Genshin Impact Proxy 3 | """ -------------------------------------------------------------------------------- /python-kcp/lkcp/__init__.py: -------------------------------------------------------------------------------- 1 | from .kcp import KcpObj -------------------------------------------------------------------------------- /python-kcp/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.so 3 | lkcp/core.c 4 | -------------------------------------------------------------------------------- /python-kcp/requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools>=20 2 | Cython>=0.24 3 | -------------------------------------------------------------------------------- /python-kcp/.gitattributes: -------------------------------------------------------------------------------- 1 | *.c linguist-language=python 2 | *.pyx linguist-language=python 3 | -------------------------------------------------------------------------------- /python-kcp/setup.sh: -------------------------------------------------------------------------------- 1 | PYTHON=python 2 | if [ $# -ge 1 ]; then 3 | PYTHON=$1 4 | fi 5 | pip install -r requirements.txt 6 | $PYTHON setup.py build 7 | $PYTHON setup.py install 8 | rm -rf build dist lkcp.egg-info 9 | PY_VERSION=`$PYTHON -V 2>&1|awk '{print $2}'|awk -F '.' '{print $1"."$2"."$3}'` 10 | echo 'install python version:' $PY_VERSION 11 | -------------------------------------------------------------------------------- /gidra/plugins/windseed_blocker.py: -------------------------------------------------------------------------------- 1 | from gidra import proto 2 | from gidra.proxy import GenshinProxy, HandlerRouter, PacketDirection 3 | from gidra.proxy.cmdids import CmdID 4 | 5 | router = HandlerRouter() 6 | 7 | 8 | @router(CmdID.WindSeedClientNotify, PacketDirection.Server) 9 | def windseed_blocker(proxy: GenshinProxy, msg: proto.WindSeedClientNotify): 10 | pass 11 | -------------------------------------------------------------------------------- /gidra/plugins/change_account.py: -------------------------------------------------------------------------------- 1 | from gidra import proto 2 | from gidra.proxy import GenshinProxy, HandlerRouter, PacketDirection 3 | from gidra.proxy.cmdids import CmdID 4 | 5 | router = HandlerRouter() 6 | 7 | 8 | @router(CmdID.GetPlayerTokenReq, PacketDirection.Client) 9 | def change_account(proxy: GenshinProxy, msg: proto.GetPlayerTokenReq): 10 | msg.account_uid = '123' 11 | msg.account_token = 'abc' 12 | proxy.send(msg, PacketDirection.Server) 13 | -------------------------------------------------------------------------------- /keys/MHYSignCN.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwt/Z98o8gbw94la07B1/ 3 | ApVCuHWHGI7Pd8FPF3PvNYf1oTYwgRczQBfPqHfXyttRRP44mqG4tfrz2zO8gXEN 4 | RSyDXtzu7dQGh3hu1t87TpPbiYcQ+ZHK58v6dy1jo30TTK64sRnjxJfWrKYDxSBx 5 | BzDbKClzqlY0J/4mVjKFxk7qS0HvoYydlRnhvJVOMdjt/SV6wyHRY66FvOvdk6BV 6 | Lom3K0WBHNcFE6ChA3GQcR+xyX1Z058AviFrx6KS45mqRujUC5vZXuwbvgrICgEV 7 | lfOScHFnrTlFX8ysM4C1bSb8Icy3V8XSb7LjCmXBeB7TUpW2vjhKlzgZeWwNu1Da 8 | EwIDAQAB 9 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /keys/MHYSignOS.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyYlF2xKeHRDZUSsXlLoS 3 | k/YAb2oIOwpbUeO4I+5GfWoybSpde4UnOlZgpKIDgltF3e9ST8bqIwuBxMoJTpOA 4 | nEKLNuBDdSeefHwhFqdczgeETxySwFKScmti1QRwgacrlgWglmaYCaeQrqbBceF9 5 | JbF4npi6S3+eFpw0j4rPjlE3vjh1AopaZQWAHGZI8Ixr7LDebe/uF8i7OCWXpkPK 6 | UTJnCEpyqM5H+pLN3MWRiL7mBR4XFqwKQr8J27Y3LN1iX9927hMsvAnh9PWoHzqp 7 | DTqIBF7w1ifYs3XQ3EMbf0zqc26UZXUaI5pD6qXNm3STz94SrfYqYY1R3Npz/Sya 8 | wwIDAQAB 9 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "gidra" 3 | version = "0.1.0" 4 | description = "Genshin Impact Proxy" 5 | authors = ["Mero"] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | betterproto = "^2.0.0b4" 10 | loguru = "^0.6.0" 11 | bottle = "^0.12.21" 12 | ec2b = {git = "https://github.com/GrownNed/ec2b.py"} 13 | requests = "^2.28.1" 14 | pycryptodome = "^3.15.0" 15 | 16 | [build-system] 17 | requires = ["poetry-core>=1.0.0"] 18 | build-backend = "poetry.core.masonry.api" 19 | -------------------------------------------------------------------------------- /gidra/plugins/checksum_bypass.py: -------------------------------------------------------------------------------- 1 | from gidra import proto 2 | from gidra.proxy import GenshinProxy, HandlerRouter, PacketDirection 3 | from gidra.proxy.cmdids import CmdID 4 | 5 | router = HandlerRouter() 6 | 7 | @router(CmdID.PlayerLoginReq, PacketDirection.Client) 8 | def change_player_checksum(proxy: GenshinProxy, msg: proto.PlayerLoginReq): 9 | #msg.checksum = "ed9fb95b179f957394ef2d984a397f35e8b31b9850496833399c259b358c9ba723" #2.8 10 | msg.checksum = "c071e821a011fe7a5f6c791d4002dc4b2ed2e864481c6fe2e9db3b6379c18f6b25" #3.0 11 | proxy.send(msg, PacketDirection.Server) 12 | -------------------------------------------------------------------------------- /python-kcp/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | from Cython.Build import cythonize 3 | 4 | ext = Extension("lkcp.core", 5 | sources = ["lkcp/core.pyx", "lkcp/ikcp.c"], 6 | ) 7 | 8 | core = cythonize(ext) 9 | 10 | setup( 11 | name = "lkcp", 12 | version = '0.1', 13 | packages = ["lkcp"], 14 | description = "python-kcp for skywind3000's kcp", 15 | author = "xingshuo", 16 | license = "MIT", 17 | url = "https://github.com/xingshuo/python-kcp.git", 18 | keywords=["kcp", "python"], 19 | ext_modules = core, 20 | ) 21 | -------------------------------------------------------------------------------- /gidra/proxy/handshake.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import struct 4 | from dataclasses import dataclass 5 | 6 | HANDSHAKE_FORMAT = ">IIIII" 7 | 8 | 9 | @dataclass 10 | class Handshake: 11 | magic1: int 12 | conv: int 13 | token: int 14 | enet: int 15 | magic2: int 16 | 17 | def __bytes__(self) -> bytes: 18 | return struct.pack( 19 | HANDSHAKE_FORMAT, 20 | self.magic1, self.conv, self.token, self.enet, self.magic2, 21 | ) 22 | 23 | @staticmethod 24 | def parse(data: bytes) -> Handshake: 25 | return Handshake(*struct.unpack(HANDSHAKE_FORMAT, data)) 26 | -------------------------------------------------------------------------------- /python-kcp/lkcp/compat.h: -------------------------------------------------------------------------------- 1 | #include "Python.h" 2 | 3 | typedef void (*capsule_dest)(PyObject *); 4 | typedef void (*cobj_dest)(void *); 5 | 6 | #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION <= 6 7 | #define CAP_NEW(a,b,c) PyCObject_FromVoidPtr(a,c) 8 | #define DEST_FUNC_TYPE cobj_dest 9 | #define CAP_GET_POINTER(a,b) PyCObject_AsVoidPtr(a) 10 | #else 11 | #define CAP_NEW PyCapsule_New 12 | #define DEST_FUNC_TYPE capsule_dest 13 | #define CAP_GET_POINTER PyCapsule_GetPointer 14 | #endif 15 | 16 | PyObject* make_capsule(void *p, const char *name, capsule_dest dest) { 17 | return CAP_NEW(p, name, (DEST_FUNC_TYPE)dest); 18 | } 19 | void* get_pointer(PyObject *cap, const char *name) { 20 | return CAP_GET_POINTER(cap, name); 21 | } 22 | -------------------------------------------------------------------------------- /gidra/plugins/change_nickname.py: -------------------------------------------------------------------------------- 1 | from gidra import proto 2 | from gidra.proxy import GenshinProxy, HandlerRouter, PacketDirection 3 | from gidra.proxy.cmdids import CmdID 4 | 5 | router = HandlerRouter() 6 | 7 | @router(CmdID.GetPlayerSocialDetailRsp, PacketDirection.Server) 8 | def change_nickname_social(proxy: GenshinProxy, msg: proto.GetPlayerSocialDetailRsp): 9 | msg.detail_data.nickname = f"{msg.detail_data.nickname}" 10 | proxy.send(msg, PacketDirection.Client) 11 | 12 | @router(CmdID.PlayerDataNotify, PacketDirection.Server) 13 | def change_nickname_data(proxy: GenshinProxy, msg: proto.PlayerDataNotify): 14 | msg.nick_name = f"{msg.nick_name}" 15 | proxy.send(msg, PacketDirection.Client) 16 | -------------------------------------------------------------------------------- /python-kcp/README.md: -------------------------------------------------------------------------------- 1 | Python-Kcp 2 | ========= 3 | Python binding for KCP, 4 | what is KCP? please visit: https://github.com/skywind3000/kcp, 5 | http://www.skywind.me/blog/archives/1048 6 | 7 | Wiki 8 | ---- 9 | 基于Cython实现KCP源码的python封装 10 | 11 | 前置库 12 | ----- 13 | 1.Python2.x / Python3.x 14 | 15 | 2.python-pip #sudo apt-get install python-pip python-dev build-essential 16 | #sudo pip install --upgrade pip 17 | 18 | 3.Cython #sudo pip install Cython 19 | 20 | 4.python-setuptools #sudo apt-get install python-setuptools 21 | 22 | 支持平台 23 | ----- 24 | Linux(Ubuntu) 25 | 26 | 安装库 27 | ----- 28 | sudo sh setup.sh / sudo sh setup.sh $YOUR_PYTHON_PATH 29 | 30 | 运行测试程序 31 | ----- 32 | python test/testkcp.py -------------------------------------------------------------------------------- /python-kcp/test/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | import sys 4 | 5 | g_ScriptStartTime = time.time() 6 | 7 | g_RndSeed = int(time.time()) 8 | 9 | def msleep(ms): 10 | time.sleep(ms * 0.001) 11 | 12 | def initrndseed(): 13 | global g_RndSeed 14 | random.seed(g_RndSeed) 15 | 16 | def rndvalue(_min, _max): 17 | return _min + random.randint(0, _max - _min) 18 | 19 | def getms(): 20 | global g_ScriptStartTime 21 | return int((time.time()-g_ScriptStartTime)*1000) 22 | 23 | def uint322netbytes(i): 24 | return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) 25 | 26 | def netbytes2uint32(s): 27 | if sys.version_info.major == 3 and isinstance(s, bytes): 28 | return s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3] 29 | else: 30 | return ord(s[0])<<24 | ord(s[1])<<16 | ord(s[2])<<8 | ord(s[3]) 31 | -------------------------------------------------------------------------------- /gidra/reader.py: -------------------------------------------------------------------------------- 1 | import io 2 | import struct 3 | 4 | 5 | class BinaryReader(io.BytesIO): 6 | def read_i16b(self) -> int: 7 | return struct.unpack(">h", self.read(2))[0] 8 | 9 | def write_i16b(self, value: int) -> None: 10 | self.write(struct.pack(">h", value)) 11 | 12 | def read_u16b(self) -> int: 13 | return struct.unpack(">H", self.read(2))[0] 14 | 15 | def write_u16b(self, value: int) -> None: 16 | self.write(struct.pack(">H", value)) 17 | 18 | def read_i32b(self) -> int: 19 | return struct.unpack(">i", self.read(4))[0] 20 | 21 | def write_i32b(self, value: int) -> None: 22 | self.write(struct.pack(">i", value)) 23 | 24 | def read_u32b(self) -> int: 25 | return struct.unpack(">I", self.read(4))[0] 26 | 27 | def write_u32b(self, value: int) -> None: 28 | self.write(struct.pack(">I", value)) 29 | -------------------------------------------------------------------------------- /python-kcp/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 richstrong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /python-kcp/lkcp/kcp.py: -------------------------------------------------------------------------------- 1 | from . import core 2 | import sys 3 | 4 | __all__ = ["KcpObj"] 5 | 6 | i = 0 7 | 8 | class KcpObj: 9 | def __init__(self, conv, token, callback): 10 | self.conv = conv 11 | self.token = token 12 | 13 | global i 14 | self.cobj = core.lkcp_create(conv, token, i, callback) 15 | i += 1 16 | 17 | def wndsize(self, sndwnd, rcvwnd): 18 | core.lkcp_wndsize(self.cobj, sndwnd, rcvwnd) 19 | 20 | def nodelay(self, nodelay, interval, resend, nc): 21 | return core.lkcp_nodelay(self.cobj, nodelay, interval, resend, nc) 22 | 23 | def check(self, current): 24 | return core.lkcp_check(self.cobj, current) 25 | 26 | def update(self, current): 27 | core.lkcp_update(self.cobj, current) 28 | 29 | def send(self, data): 30 | if sys.version_info.major == 3 and isinstance(data, str): 31 | data = data.encode("UTF-8") 32 | return core.lkcp_send(self.cobj, data) 33 | 34 | def input(self, data): 35 | return core.lkcp_input(self.cobj, data) 36 | 37 | def recv(self): 38 | return core.lkcp_recv(self.cobj) 39 | 40 | def flush(self): 41 | core.lkcp_flush(self.cobj) 42 | 43 | def setmtu(self, mtu): 44 | core.lkcp_setmtu(self.cobj, mtu) 45 | -------------------------------------------------------------------------------- /gidra/plugins/seed_exchange.py: -------------------------------------------------------------------------------- 1 | from gidra import proto 2 | from gidra.proxy import GenshinProxy, HandlerRouter, PacketDirection 3 | from gidra.proxy.cmdids import CmdID 4 | from gidra.mhycrypt import encrypt_and_sign, decrypt, new_key 5 | 6 | import base64 7 | 8 | router = HandlerRouter() 9 | 10 | @router(CmdID.GetPlayerTokenReq, PacketDirection.Client) 11 | def handle_token_req(proxy: GenshinProxy, msg: proto.GetPlayerTokenReq): 12 | client_seed_bytes = decrypt(base64.b64decode(msg.client_seed), "SigningKey") 13 | 14 | encrypted_data, _ = encrypt_and_sign(client_seed_bytes, "MHYSignOS" if (msg.key_id == 3) else "MHYSignCN") 15 | msg.client_seed = base64.b64encode(encrypted_data).decode() 16 | 17 | proxy.client_seed = int.from_bytes(client_seed_bytes, byteorder='big', signed=False) 18 | proxy.send(msg, PacketDirection.Server) 19 | 20 | @router(CmdID.GetPlayerTokenRsp, PacketDirection.Server) 21 | def handle_token_rsp(proxy: GenshinProxy, msg: proto.GetPlayerTokenRsp): 22 | server_seed_bytes = decrypt(base64.b64decode(msg.encrypted_seed), msg.key_id) 23 | server_seed = int.from_bytes(server_seed_bytes, byteorder='big', signed=False) 24 | 25 | seed = server_seed ^ proxy.client_seed 26 | 27 | _, sign = encrypt_and_sign(server_seed_bytes, msg.key_id) 28 | msg.seed_signature = base64.b64encode(sign).decode() 29 | 30 | proxy.send(msg, PacketDirection.Client) 31 | proxy.key = new_key(seed) -------------------------------------------------------------------------------- /python-kcp/test/latencysm.py: -------------------------------------------------------------------------------- 1 | from utils import * 2 | 3 | class DelayPacket: 4 | def __init__(self, ptr): 5 | self.ptr = ptr 6 | 7 | def getdata(self): 8 | return self.ptr 9 | 10 | def setts(self,ts): 11 | self.ts = ts 12 | 13 | def getts(self): 14 | return self.ts 15 | 16 | class LatencySimulator: 17 | def __init__(self, lostrate=10, rttmin=60, rttmax=125): 18 | self.lostrate = lostrate/2 19 | self.rttmin = rttmin/2 20 | self.rttmax = rttmax/2 21 | self.tunnel12 = [] 22 | self.tunnel21 = [] 23 | 24 | def clear(self): 25 | self.tunnel12 = [] 26 | self.tunnel21 = [] 27 | 28 | def send(self, send_peer, data): 29 | if rndvalue(1,100) <= self.lostrate: 30 | return 31 | pkg = DelayPacket(data) 32 | delay = rndvalue(self.rttmin, self.rttmax) 33 | current = getms() 34 | pkg.setts(current+delay) 35 | if send_peer == 1: 36 | self.tunnel12.append(pkg) 37 | else: 38 | self.tunnel21.append(pkg) 39 | 40 | def recv(self, recv_peer): 41 | current = getms() 42 | tunnel = None 43 | if recv_peer == 1: 44 | tunnel = self.tunnel21 45 | else: 46 | tunnel = self.tunnel12 47 | if len(tunnel) <= 0: 48 | return -1,None 49 | pkg = tunnel[0] 50 | if pkg.getts() > current: 51 | return -1,None 52 | tunnel.pop(0) 53 | data = pkg.getdata() 54 | return len(data),data 55 | -------------------------------------------------------------------------------- /keys/MHYPrivCN.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAz/fyfozlDIDWG9e3Lb29+7j3c66wvUJBaBWP10rB9HTE6prj 3 | fcGMqC9imr6zAdD9q+Gr1j7egvqgi3Da+VBAMFH92/5wD5PsD7dX8Z2f4o65Vk2n 4 | VOY8Dl75Z/uRhg0Euwnfrved69z9LG6utmlyv6YUPAflXh/JFw7Dq6c4EGeR+Kej 5 | FTwmVhEdzPGHjXhFmsVt9HdXRYSf4NxHPzOwj8tiSaOQA0jC4E4mM7rvGSH5GX6h 6 | ma+7pJnl/5+rEVM0mSQvm0m1XefmuFy040bEZ/6O7ZenOGBsvvwuG3TT4FNDNzW8 7 | Dw9ExH1l6NoRGaVkDdtrl/nFu5+a09Pm/E0ElwIDAQABAoIBAQCtH17Cck+KJQYX 8 | j29xqG4qykNUDawbILiKCMkBE7553Wq/UcjmuuR4bVnML8ucS3mgR/BgHV3l8vUK 9 | nxvqRx/oGZkWNazbiuwL+ThAblLWqrEmYuZVCoQcAnvkT8tIqDWz7fhDEuZnnkMz 10 | ZcATIZzgZUSa5IfP3u3rP+MrVbyaCdzJEeI0Yrv1XT+M5ddkKQrYgqC5kRiYi/Lj 11 | NcLJhqSVt8p37CdJx1PGHFjKKb4MZpANlNRgeTtWpGVfS0PJLzaiI1NyPSJv7xWZ 12 | gVhbK9+wQxqSG6KmZ4vpEvRI1zKiov5BsAFN+GfuD5mpn1Xo9CpzTfj/sO13VpHH 13 | +Mt80+yBAoGBAPYXVEcXug5zqkqXup4dp1S05saz1zWPhUhQm+CrbhgeTqpjngJJ 14 | EB79qMrGmyki0P/cGtbTcrHf8+i7gDlIGW0OMb4/jn4f5ACVD00iyvkHSGPn0Aim 15 | MoNOMbkGot7SkSnncwxXdawwDyTu2dofXuBr72+GYqgRAG52IuA0C0pRAoGBANhX 16 | p/UyW/htB27frKch/rTKQKm12kBV20AkkRUQUibiiQyWueWKs+5bVaW5R5oDIhWx 17 | qftJtnEFWUvWaTHpHsB/bpjS3CJ6WknqNbpa3QIScpV1uw8V+Etz/K2/ftjyZzFo 18 | nqc+Jud5364xFdIlOsRj9gZnK83Wcui6EFxAer5nAoGBAJzTzzSjLUHqejqhKR98 19 | nFeCFZPJpjuO5AxqunvaJAYgwlcZtueT8j8dvgTDvrvfYTu85CnFhNFQfFrzqspW 20 | ZUW3hwHL9R3xatboJ2Er7Bf5iSuJ3my0pXpCSbO1Q/QmUrZWtl3GGsqJsg0CXjkA 21 | RvFUN7ll9ddPRmwewykIYa2RAoGAcmKuWFNfE1O6WWIELH4p6LcDR3fyRI/gk+KB 22 | nyx48zxVkAVllrsmdYFvIGd9Ny4u6F9+a3HG960HULS1/ACxFMCL3lumrsgYUvp1 23 | m+mM7xqH4QRVeh14oZRa5hbY36YS76nMMMsI0Ny8aqJjUjADCXF81FfabkPTj79J 24 | BS3GeEMCgYAXmFIT079QokHjJrWz/UaoEUbrNkXB/8vKiA4ZGSzMtl3jUPQdXrVf 25 | e0ofeKiqCQs4f4S0dYEjCv7/OIijV5L24mj/Z1Q4q++S5OksKLPPAd3gX4AYbRcg 26 | PS4rUKl1oDk/eYN0CNYC/DYV9sAv65lX8b35HYhvXISVYtwwQu/+Yg== 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /keys/MHYPrivOS.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA02M1I1V/YvxANOvLFX8R7D8At40IlT7HDWpAW3t+tAgQ7sqj 3 | CeYOxiXqOaaw2kJhM3HT5nZll48UmykVq45Q05J57nhdSsGXLJshtLcTg9liMEoW 4 | 61BjVZi9EPPRSnE05tBJc57iqZw+aEcaSU0awfzBc8IkRd6+pJ5iIgEVfuTluani 5 | zhHWvRli3EkAF4VNhaTfP3EkYfr4NE899aUeScbbdLFI6u1XQudlJCPTxaISx5Zc 6 | wM+nP3v242ABcjgUcfCbz0AY547WazK4bWP3qicyxo4MoLOoe9WBq6EuG4CuZQrz 7 | Knq8ltSxud/6chdg8Mqp/IasEQ2TpvY78tEXDQIDAQABAoIBAQC4uPsYk4AsSe75 8 | 0Au6Dz7kSfIgdDhJ44AisvTmfLauMFZLtfxfjBDhCwTxuD7XnCZAxHm97Ty+AqSp 9 | Km/raQQsvtWalMhBqYanzjDYMRv2niJ1vGjm3WrQxBaEF+yOtvrZsK5fQTslqInI 10 | qknIQH7fgjazJ7Z28D18sYNj37qfFWSSymgFo+SoS/BKEr200lpRA/oaGXiHcyIO 11 | jJidP6b7UGes7uhMXUvLrfozmCsSqslxXO5Uk5XN/fWl4LxCGX7mpNfPZIT5YBSj 12 | HliFkNlxIjyJg8ORLGi82M2cuyxp39r93F6uaCjLtb+rdwlGur7npgXUkKfWQJf9 13 | WE7uar6BAoGBAPXIuIuYFFUhqNz5CKU014jZu6Ql0z5ZA08V84cTJcfLIK4e2rqC 14 | 8DFTldA0FtVfOGt0V08H/x2pRChGOvUwGG5nn9Dqqh6BjByUrW4z2hnXzT3ZuSDh 15 | 6eapiCB1jl9meJ0snhF2Ps/hqWGL2b3SkCCe90qVTzOVOeLO6YUCIOq9AoGBANws 16 | fQkAq/0xw8neRGNTrnXimvbS+VXPIF38widljubNN7DY5cIFTQJrnTBWKbuz/t9a 17 | J8QX6TFL0ci/9vhPJoThfL12vL2kWGYgWkWRPmqaBW3yz7Hs5rt+xuH3/7A5w5vm 18 | kEg1NZJgnsJ0rMUTu1Q6PM5CBg6OpyHY4ThBb8qRAoGAML8ciuMgtTm1yg3CPzHZ 19 | xZSZeJbf7K+uzlKmOBX+GkAZPS91ZiRuCvpu7hpGpQ77m6Q5ZL1LRdC6adpz+wkM 20 | 72ix87d3AhHjfg+mzgKOsS1x0WCLLRBhWZQqIXXvRNCH/3RH7WKsVoKFG4mnJ9TJ 21 | LQ8aMLqoOKzSDD/JZM3lRWkCgYA8hn5Y2zZshCGufMuQApETFxhCgfzI+geLztAQ 22 | xHpkOEX296kxjQN+htbPUuBmGTUXcVE9NtWEF7Oz3BGocRnFrbb83odEGsmySXKH 23 | bUYbR/v2Ham638UOBevmcqZ3a2m6kcdYEkiH1MfP7QMRqjr1DI1qpfvERLLtOxGu 24 | xU5WAQKBgQCaVavyY6Slj3ZRQ7iKk9fHkge/bFl+zhANxRfWVOYMC8mD2gHDsq9C 25 | IdCp1Mg0tJpWLaGgyDM1kgChZYsff4jRxHC4czvAtoPSlxWXF2nL31qJ3vk2Zzzc 26 | a4GSHAInodXBrKstav5SIKosWKT2YysxgHlA9Sm2f4n09GjFbslEHg== 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /keys/SigningKey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAxbbx2m1feHyrQ7jP+8mtDF/pyYLrJWKWAdEv3wZrOtjOZzeL 3 | GPzsmkcgncgoRhX4dT+1itSMR9j9m0/OwsH2UoF6U32LxCOQWQD1AMgIZjAkJeJv 4 | FTrtn8fMQ1701CkbaLTVIjRMlTw8kNXvNA/A9UatoiDmi4TFG6mrxTKZpIcTInvP 5 | EpkK2A7Qsp1E4skFK8jmysy7uRhMaYHtPTsBvxP0zn3lhKB3W+HTqpneewXWHjCD 6 | fL7Nbby91jbz5EKPZXWLuhXIvR1Cu4tiruorwXJxmXaP1HQZonytECNU/UOzP6GN 7 | Ldq0eFDE4b04Wjp396551G99YiFP2nqHVJ5OMQIDAQABAoIBAQDEeYZhjyq+avUu 8 | eSuFhOaIU4/ZhlXycsOqzpwJvzEz61tBSvrZPA5LSb9pzAvpic+7hDH94jX89+8d 9 | NfO7qlADsVNEQJBxuv2o1MCjpCRkmBZz506IBGU60Kt1j5kwdCEergTW1q375z4w 10 | l8f7LmSL2U6WvKcdojTVxohBkIUJ7shtmmukDi2YnMfe6T/2JuXDDL8rvIcnfr5E 11 | MCgPQs+xLeLEGrIJdpUy1iIYZYrzvrpJwf9EJL3D0e7jkpbvAQZ8EF9YhEizJhOm 12 | dzTqW4PgW2yUaHYd3q5QjiILy7AC+oOYoTZln3RfjPOxl+bYjeMOWlqkgtpPQkAE 13 | 4I64w8RZAoGBAPLR44pEkmTdfIIF8ZtzBiVfDZ29bT96J0CWXGVzp8x6bSu5J5jl 14 | s7sP8DEcjGZ6vHsLGOvkcNxzcnR3l/5HOz6TIuvVuUm36b1jHltq1xZStjGeKZs1 15 | ihhJSu2lIA+TrK8FCRnKARJ0ughXGNZFItgeM230Sgjp2RL4ISXJ724XAoGBANBy 16 | S2RwNpUYvkCSZHSFnQM/jq1jldxw+0p4jAGpWLilEaA/8xWUnZrnCrPFF/t9llpb 17 | dTR/dCI8ntIMAy2dH4IUHyYKUahyHSzCAUNKpS0s433kn5hy9tGvn7jyuOJ4dk9F 18 | o1PIZM7qfzmkdCBbX3NF2TGpzOvbYGJHHC3ssVr3AoGBANHJDopN9iDYzpJTaktA 19 | VEYDWnM2zmUyNylw/sDT7FwYRaup2xEZG2/5NC5qGM8NKTww+UYMZom/4FnJXXLd 20 | vcyxOFGCpAORtoreUMLwioWJzkkN+apT1kxnPioVKJ7smhvYAOXcBZMZcAR2o0m0 21 | D4eiiBJuJWyQBPCDmbfZQFffAoGBAKpcr4ewOrwS0/O8cgPV7CTqfjbyDFp1sLwF 22 | 2A/Hk66dotFBUvBRXZpruJCCxn4R/59r3lgAzy7oMrnjfXl7UHQk8+xIRMMSOQwK 23 | p7OSv3szk96hy1pyo41vJ3CmWDsoTzGs7bcdMl72wvKemRaU92ckMEZpzAT8cEMC 24 | cWKLb8yzAoGAMibG8IyHSo7CJz82+7UHm98jNOlg6s73CEjp0W/+FL45Ka7MF/lp 25 | xtR3eSmxltvwvjQoti3V4Qboqtc2IPCt+EtapTM7Wo41wlLCWCNx4u25pZPH/c8g 26 | 1yQ+OvH+xOYG+SeO98Phw/8d3IRfR83aqisQHv5upo2Rozzo0Kh3OsE= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /gidra/proxy/packet.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import betterproto 4 | 5 | from gidra import proto 6 | from gidra.proxy.cmdids import CmdID 7 | from gidra.reader import BinaryReader 8 | 9 | 10 | PACKET_MAGIC = (0x4567, 0x89ab) 11 | 12 | 13 | class Packet: 14 | def __init__(self, head: proto.PacketHead = None, body: betterproto.Message = None): 15 | self.head = head 16 | if not head: 17 | self.head = proto.PacketHead() 18 | 19 | self.body = body 20 | if body: 21 | self.cmdid = CmdID[body.__class__.__name__] 22 | 23 | def parse(self, data: bytes) -> Packet: 24 | buf = BinaryReader(data) 25 | 26 | magic1 = buf.read_u16b() 27 | if magic1 != PACKET_MAGIC[0]: 28 | raise Exception 29 | 30 | self.cmdid = CmdID(buf.read_u16b()) 31 | metadata_len = buf.read_u16b() 32 | data_len = buf.read_u32b() 33 | 34 | self.head = proto.PacketHead().parse(buf.read(metadata_len)) 35 | 36 | proto_class = getattr(proto, self.cmdid.name, None) 37 | 38 | if data_len: 39 | self.body = proto_class().parse(buf.read(data_len)) 40 | else: 41 | self.body = proto_class() 42 | 43 | magic2 = buf.read_u16b() 44 | if magic2 != PACKET_MAGIC[1]: 45 | raise Exception 46 | 47 | return self 48 | 49 | def __bytes__(self) -> bytes: 50 | if not self.body: 51 | raise Exception 52 | 53 | head_bytes = bytes(self.head) 54 | body_bytes = bytes(self.body) 55 | 56 | buf = BinaryReader() 57 | 58 | buf.write_u16b(PACKET_MAGIC[0]) 59 | buf.write_u16b(self.cmdid) 60 | 61 | buf.write_u16b(len(head_bytes)) 62 | buf.write_u32b(len(body_bytes)) 63 | 64 | buf.write(head_bytes) 65 | buf.write(body_bytes) 66 | 67 | buf.write_u16b(PACKET_MAGIC[1]) 68 | 69 | return buf.getvalue() 70 | -------------------------------------------------------------------------------- /gidra/mhycrypt/__init__.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | from typing import Tuple 3 | 4 | from .mt64 import mt64 5 | 6 | from Crypto.PublicKey import RSA 7 | from Crypto.Cipher import PKCS1_v1_5 8 | from Crypto.Signature import pkcs1_15 9 | from Crypto.Hash import SHA256 10 | 11 | import os 12 | 13 | keys = {} 14 | 15 | def init_keys(path: str): 16 | with open(os.path.join(path, 'MHYPrivCN.pem'), 'r') as f: 17 | keys[2] = RSA.import_key(f.read()) 18 | 19 | with open(os.path.join(path, 'MHYPrivOS.pem'), 'r') as f: 20 | keys[3] = RSA.import_key(f.read()) 21 | 22 | with open(os.path.join(path, 'SigningKey.pem'), 'r') as f: 23 | keys["SigningKey"] = RSA.import_key(f.read()) 24 | 25 | with open(os.path.join(path, 'MHYSignOS.pem'), 'r') as f: 26 | keys["MHYSignOS"] = RSA.import_key(f.read()) 27 | 28 | with open(os.path.join(path, 'MHYSignCN.pem'), 'r') as f: 29 | keys["MHYSignCN"] = RSA.import_key(f.read()) 30 | 31 | 32 | def new_key(seed: int) -> bytes: 33 | mt = mt64() 34 | mt.seed(seed) 35 | 36 | mt.seed(mt.int64()) 37 | mt.int64() 38 | 39 | return bytes(byte for _ in range(512) for byte in mt.int64().to_bytes(8, "big")) 40 | 41 | def xor(data: bytes, key: bytes) -> bytes: 42 | return bytes(v ^ k for (v, k) in zip(data, cycle(key))) 43 | 44 | def decrypt(data: bytes, key_id) -> bytes: 45 | key = keys[key_id] 46 | dec = PKCS1_v1_5.new(key) 47 | 48 | chunk_size = 256 49 | out = b'' 50 | 51 | for i in range(0, len(data), chunk_size): 52 | chunk = data[i:i + chunk_size] 53 | out += dec.decrypt(chunk, None) 54 | 55 | return out 56 | 57 | def do_sign(message, key): 58 | signer = pkcs1_15.new(key) 59 | digest = SHA256.new(message) 60 | return signer.sign(digest) 61 | 62 | def encrypt_and_sign(data: bytes, key_id) -> Tuple[bytes, bytes]: 63 | sign_key = keys["SigningKey"] 64 | 65 | key = keys[key_id] 66 | enc = PKCS1_v1_5.new(key) 67 | 68 | chunk_size = 256 - 11 69 | out = b'' 70 | 71 | if len(data) > chunk_size: 72 | for i in range(0, len(data), chunk_size): 73 | chunk = data[i:i + chunk_size] 74 | out += enc.encrypt(chunk) 75 | else: 76 | out = enc.encrypt(data) 77 | 78 | signature = do_sign(data, sign_key) 79 | 80 | return out, signature -------------------------------------------------------------------------------- /gidra/__main__.py: -------------------------------------------------------------------------------- 1 | from gidra.plugins import (change_account, change_nickname, 2 | commands, windseed_blocker, seed_exchange, checksum_bypass) 3 | 4 | 5 | from gidra.proxy import GenshinProxy, PacketDirection 6 | from gidra.proto import QueryCurrRegionHttpRsp 7 | from gidra.mhycrypt import init_keys, decrypt, encrypt_and_sign 8 | import ec2b 9 | 10 | from bottle import route, run, request 11 | import requests 12 | import base64 13 | 14 | PROXY_GATESERVER = ('127.0.0.1', 8888) 15 | 16 | #Change these according to your account's region 17 | TARGET_DISPATCH_URL = 'https://oseurodispatch.yuanshen.com/query_cur_region' 18 | TARGET_GATESERVER = ('47.245.143.151', 22102) 19 | 20 | @route('/query_cur_region') 21 | def handle_query_cur(): 22 | # Trick to bypass system proxy, this way we don't need to hardcode the ec2b key 23 | session = requests.Session() 24 | session.trust_env = False 25 | 26 | r = session.get(f'{TARGET_DISPATCH_URL}?{request.query_string}') 27 | 28 | if any(map(request.query.version.__contains__, ["2.7.5", "2.8", "2.8.5", "3.0"])): 29 | 30 | key_id = int(request.query.key_id) 31 | respdata = r.json() 32 | respdec = decrypt(base64.b64decode(respdata['content']), key_id) 33 | 34 | proto = QueryCurrRegionHttpRsp() 35 | proto.parse(respdec) 36 | 37 | if proto.retcode == 0: 38 | proto.region_info.gateserver_ip, proto.region_info.gateserver_port = PROXY_GATESERVER 39 | proxy.key = ec2b.derive(proto.client_secret_key) 40 | 41 | enc_data, sign = encrypt_and_sign(bytes(proto), key_id) 42 | return {'content': base64.b64encode(enc_data).decode(), 'sign': base64.b64encode(sign).decode()} 43 | 44 | else: 45 | proto = QueryCurrRegionHttpRsp() 46 | proto.parse(base64.b64decode(r.text)) 47 | 48 | if proto.retcode == 0: 49 | proto.region_info.gateserver_ip, proto.region_info.gateserver_port = PROXY_GATESERVER 50 | proxy.key = ec2b.derive(proto.client_secret_key) 51 | 52 | return base64.b64encode(bytes(proto)).decode() 53 | 54 | proxy = GenshinProxy(PROXY_GATESERVER, TARGET_GATESERVER) 55 | 56 | def main(): 57 | init_keys("./keys") 58 | #proxy.add(change_account.router) 59 | #proxy.add(change_nickname.router) 60 | proxy.add(windseed_blocker.router) 61 | proxy.add(seed_exchange.router) 62 | proxy.add(checksum_bypass.router) 63 | #proxy.add(commands.router) 64 | 65 | proxy.start() 66 | 67 | run(host='127.0.0.1', port=8081, debug=False) 68 | 69 | if __name__ == '__main__': 70 | main() 71 | -------------------------------------------------------------------------------- /gidra/mhycrypt/mt64.py: -------------------------------------------------------------------------------- 1 | class mt64: 2 | def __init__(self): 3 | self.mt = [0]*312 4 | self.mti = 313 5 | 6 | def seed(self, seed): 7 | self.mt[0] = seed & 0xffffffffffffffff 8 | for i in range(1,312): 9 | self.mt[i] = (6364136223846793005 * (self.mt[i-1] ^ (self.mt[i-1] >> 62)) + i) & 0xffffffffffffffff 10 | self.mti = 312 11 | 12 | def init_by_array(self, key): 13 | self.seed(19650218) 14 | i = 1 15 | j = 0 16 | k = max(312, len(key)) 17 | for ki in range(k): 18 | self.mt[i] = ((self.mt[i] ^ ((self.mt[i-1] ^ (self.mt[i-1] >> 62)) * 3935559000370003845)) + key[j] + j) & 0xffffffffffffffff 19 | i += 1 20 | j += 1 21 | if i >= 312: 22 | self.mt[0] = self.mt[311] 23 | i = 1 24 | if j >= len(key): 25 | j = 0 26 | for ki in range(312): 27 | self.mt[i] = ((self.mt[i] ^ ((self.mt[i-1] ^ (self.mt[i-1] >> 62)) * 2862933555777941757)) - i) & 0xffffffffffffffff 28 | i += 1 29 | if i >= 312: 30 | self.mt[0] = self.mt[311] 31 | i = 1 32 | self.mt[0] = 1 << 63 33 | 34 | def int64(self): 35 | if self.mti >= 312: 36 | if self.mti == 313: 37 | self.seed(5489) 38 | 39 | for k in range(311): 40 | y = (self.mt[k] & 0xFFFFFFFF80000000) | (self.mt[k+1] & 0x7fffffff) 41 | if k < 312 - 156: 42 | self.mt[k] = self.mt[k+156] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 43 | else: 44 | self.mt[k] = self.mt[k+156-624] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 45 | 46 | y = (self.mt[311] & 0xFFFFFFFF80000000) | (self.mt[0] & 0x7fffffff) 47 | self.mt[311] = self.mt[155] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 48 | self.mti = 0 49 | 50 | y = self.mt[self.mti] 51 | self.mti += 1 52 | 53 | y ^= (y >> 29) & 0x5555555555555555 54 | y ^= (y << 17) & 0x71D67FFFEDA60000 55 | y ^= (y << 37) & 0xFFF7EEE000000000 56 | y ^= (y >> 43) 57 | 58 | return y 59 | 60 | def int64b(self): 61 | if self.mti == 313: 62 | self.seed(5489) 63 | 64 | k = self.mti 65 | 66 | if k == 312: 67 | k = 0 68 | self.mti = 0 69 | 70 | if k == 311: 71 | y = (self.mt[311] & 0xFFFFFFFF80000000) | (self.mt[0] & 0x7fffffff) 72 | self.mt[311] = self.mt[155] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 73 | else: 74 | y = (self.mt[k] & 0xFFFFFFFF80000000) | (self.mt[k+1] & 0x7fffffff) 75 | if k < 312 - 156: 76 | self.mt[k] = self.mt[k+156] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 77 | else: 78 | self.mt[k] = self.mt[k+156-624] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 79 | 80 | y = self.mt[self.mti] 81 | self.mti += 1 82 | 83 | y ^= (y >> 29) & 0x5555555555555555 84 | y ^= (y << 17) & 0x71D67FFFEDA60000 85 | y ^= (y << 37) & 0xFFF7EEE000000000 86 | y ^= (y >> 43) 87 | 88 | return y 89 | -------------------------------------------------------------------------------- /python-kcp/test/testkcp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import lkcp 3 | from lkcp import KcpObj 4 | from utils import * 5 | from latencysm import LatencySimulator 6 | import sys 7 | 8 | g_oLsm = None 9 | 10 | _input = input if sys.version_info.major == 3 else raw_input 11 | 12 | def test(mode): 13 | global g_oLsm 14 | initrndseed() 15 | g_oLsm = LatencySimulator(25, 80, 150) 16 | conv = 123 17 | token = 321 18 | p1 = 1 19 | p2 = 2 20 | okcp1 = KcpObj(conv, token, lambda _, x: g_oLsm.send(1, x)) 21 | okcp2 = KcpObj(conv, token, lambda _, x: g_oLsm.send(2, x)) 22 | start_ts = getms() 23 | slap = start_ts + 20 24 | index = 0 25 | inext = 0 26 | count = 0 27 | sumrtt = 0 28 | maxrtt = 0 29 | okcp1.wndsize(128,128) 30 | okcp2.wndsize(128,128) 31 | 32 | if mode == 0: 33 | okcp1.nodelay(0, 10, 0, 0) 34 | okcp2.nodelay(0, 10, 0, 0) 35 | elif mode == 1: 36 | okcp1.nodelay(0, 10, 0, 1) 37 | okcp2.nodelay(0, 10, 0, 1) 38 | else: 39 | okcp1.nodelay(1, 10, 2, 1) 40 | okcp2.nodelay(1, 10, 2, 1) 41 | 42 | while True: 43 | current = getms() 44 | nextt1 = okcp1.check(current) 45 | nextt2 = okcp2.check(current) 46 | nextt = min(nextt1, nextt2) 47 | diff = nextt - current 48 | if diff > 0: 49 | msleep(diff) 50 | current = getms() 51 | 52 | okcp1.update(current) 53 | okcp2.update(current) 54 | 55 | ##每隔 20ms,okcp1发送数据 56 | while current >= slap: 57 | s1 = uint322netbytes(index) 58 | s2 = uint322netbytes(current) 59 | okcp1.send(s1+s2) 60 | slap += 20 61 | index += 1 62 | 63 | #处理虚拟网络:检测是否有udp包从p1->p2 64 | while True: 65 | ilen,pkg = g_oLsm.recv(p2) 66 | if ilen < 0: 67 | break 68 | #如果 p2收到udp,则作为下层协议输入到okcp2 69 | okcp2.input(pkg) 70 | 71 | #处理虚拟网络:检测是否有udp包从p2->p1 72 | while True: 73 | ilen,pkg = g_oLsm.recv(p1) 74 | if ilen < 0: 75 | break 76 | #如果 p1收到udp,则作为下层协议输入到okcp1 77 | okcp1.input(pkg) 78 | 79 | #okcp2接收到任何包都返回回去 80 | while True: 81 | ilen,pkg = okcp2.recv() 82 | if ilen <= 0: 83 | break 84 | okcp2.send(pkg) 85 | 86 | #okcp1收到okcp2的回射数据 87 | while True: 88 | ilen,pkg = okcp1.recv() 89 | if ilen <= 0: 90 | break 91 | sn = netbytes2uint32(pkg[:4]) 92 | ts = netbytes2uint32(pkg[4:8]) 93 | rtt = current - ts 94 | if sn != inext: 95 | print("ERROR sn count %d %d!=%d\n"%(count, sn, inext)) 96 | return 97 | inext += 1 98 | sumrtt += rtt 99 | count += 1 100 | if rtt > maxrtt: 101 | maxrtt = rtt 102 | print("[RECV] mode=%d sn=%d rtt=%d\n"%(mode, sn, rtt)) 103 | 104 | if inext > 20: 105 | break 106 | 107 | cost = getms() - start_ts 108 | print("mode %d total %dms avgrtt=%d maxrtt=%d\n"%(mode, cost, sumrtt/count, maxrtt)) 109 | del okcp1 110 | del okcp2 111 | 112 | test(0) #默认模式,类似 TCP:正常模式,无快速重传,常规流控 113 | _input("press enter to next") 114 | test(1) #普通模式,关闭流控等 115 | _input("press enter to next") 116 | test(2) #快速模式,所有开关都打开,且关闭流控 -------------------------------------------------------------------------------- /gidra/plugins/commands.py: -------------------------------------------------------------------------------- 1 | from loguru import logger 2 | 3 | from gidra import proto 4 | from gidra.proxy import GenshinProxy, HandlerRouter, PacketDirection 5 | from gidra.proxy.cmdids import CmdID 6 | 7 | router = HandlerRouter() 8 | 9 | uid = None 10 | enter_scene_token = None 11 | 12 | 13 | @router(CmdID.GetPlayerTokenRsp, PacketDirection.Server) 14 | def get_player_token_rsp(proxy: GenshinProxy, msg: proto.GetPlayerTokenRsp): 15 | global uid 16 | uid = msg.uid 17 | 18 | proxy.send(msg, PacketDirection.Client) 19 | 20 | 21 | @router(CmdID.SceneInitFinishRsp, PacketDirection.Server) 22 | def scene_init_finish_rsp(proxy: GenshinProxy, msg: proto.SceneInitFinishRsp): 23 | global enter_scene_token 24 | enter_scene_token = msg.enter_scene_token 25 | 26 | msg.retcode = None 27 | proxy.send(msg, PacketDirection.Client) 28 | 29 | 30 | @router(CmdID.EnterSceneReadyRsp, PacketDirection.Server) 31 | def enter_scene_ready_rsp(proxy: GenshinProxy, msg: proto.EnterSceneReadyRsp): 32 | msg.retcode = None 33 | proxy.send(msg, PacketDirection.Client) 34 | 35 | 36 | @router(CmdID.EnterSceneDoneRsp, PacketDirection.Server) 37 | def enter_scene_done_rsp(proxy: GenshinProxy, msg: proto.EnterSceneDoneRsp): 38 | msg.retcode = None 39 | proxy.send(msg, PacketDirection.Client) 40 | 41 | 42 | @router(CmdID.EnterSceneReadyRsp, PacketDirection.Server) 43 | def enter_scene_ready_rsp(proxy: GenshinProxy, msg: proto.EnterSceneReadyRsp): 44 | msg.retcode = None 45 | proxy.send(msg, PacketDirection.Client) 46 | 47 | 48 | @router(CmdID.PostEnterSceneRsp, PacketDirection.Server) 49 | def post_enter_scene_rsp(proxy: GenshinProxy, msg: proto.PostEnterSceneRsp): 50 | msg.retcode = None 51 | proxy.send(msg, PacketDirection.Client) 52 | 53 | 54 | @router(CmdID.MarkMapReq, PacketDirection.Client) 55 | def tp_with_mark(proxy: GenshinProxy, msg: proto.MarkMapReq): 56 | mark = msg.mark 57 | 58 | if msg.op != proto.MarkMapReqOperation.ADD or not mark.name.startswith('!'): 59 | proxy.send(msg, PacketDirection.Server) 60 | return 61 | 62 | global uid 63 | global enter_scene_token 64 | 65 | command, *args = mark.name[1:].split() 66 | match command: 67 | case 'tp': 68 | pos = proto.Vector(x=mark.pos.x, y=int(args[0]), z=mark.pos.z) 69 | player_enter_scene_notify = proto.PlayerEnterSceneNotify( 70 | scene_id=msg.mark.scene_id, 71 | pos=pos, 72 | type=proto.EnterType.ENTER_GOTO_BY_PORTAL, 73 | target_uid=uid, 74 | world_level=3, 75 | enter_scene_token=enter_scene_token, 76 | is_first_login_enter_scene=False, 77 | scene_tag_id_list=[107, 113, 117, 125], 78 | enter_reason=1, 79 | world_type=1, 80 | ) 81 | proxy.send(player_enter_scene_notify, PacketDirection.Client) 82 | case 'join': 83 | player_apply_enter_mp_req = proto.PlayerApplyEnterMpReq(target_uid=int(args[0])) 84 | proxy.send(player_apply_enter_mp_req, PacketDirection.Server) 85 | case 'open_state': 86 | set_open_state_req = proto.SetOpenStateReq(key=int(args[0])) 87 | proxy.send(set_open_state_req, PacketDirection.Server) 88 | 89 | 90 | @router(CmdID.PrivateChatReq, PacketDirection.Client) 91 | def chat_request(proxy: GenshinProxy, msg: proto.PrivateChatReq): 92 | if not msg.text.startswith('!'): 93 | proxy.send(msg, PacketDirection.Server) 94 | return 95 | 96 | command, *args = msg.text[1:].split() 97 | match command: 98 | case 'join': 99 | player_apply_enter_mp_req = proto.PlayerApplyEnterMpReq(target_uid=int(args[0])) 100 | proxy.send(player_apply_enter_mp_req, PacketDirection.Server) 101 | -------------------------------------------------------------------------------- /gidra/proxy/kcp_socket.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | import random 3 | import socket 4 | import threading 5 | import time 6 | 7 | from lkcp import KcpObj 8 | from loguru import logger 9 | from gidra.proxy.handshake import Handshake 10 | 11 | _Address = tuple[str, int] 12 | _BUFFER_SIZE = 1 << 16 13 | 14 | 15 | class KcpSocket: 16 | def __init__(self): 17 | self._time = time.time() 18 | self.recv_queue = deque() 19 | self.recv_queue_semaphore = threading.Semaphore(0) 20 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 21 | 22 | def _get_time(self) -> int: 23 | return time.time() - self._time 24 | 25 | def _kcp_update(self): 26 | while self.kcp: 27 | current_time = int(self._get_time() * 1000) 28 | self.kcp.update(current_time) 29 | 30 | next_time = self.kcp.check(current_time) 31 | diff = next_time - current_time 32 | 33 | if diff > 0: 34 | time.sleep(diff / 1000) 35 | 36 | def _kcp_recv(self): 37 | while self.kcp: 38 | data = self.sock.recv(_BUFFER_SIZE) 39 | self.kcp.input(data) 40 | 41 | while x := self.kcp.recv()[1]: 42 | self.recv_queue.append(x) 43 | self.recv_queue_semaphore.release() 44 | 45 | def connect(self, addr: _Address) -> bool: 46 | self.sock.connect(addr) 47 | self.addr = addr 48 | 49 | hs1 = Handshake(0xff, 0, 0, 1234567890, 0xffffffff) 50 | self.sock.send(bytes(hs1)) 51 | logger.debug('[S] handshake sended') 52 | 53 | data = self.sock.recv(_BUFFER_SIZE) 54 | hs2 = Handshake.parse(data) 55 | logger.debug('[S] handshake received') 56 | 57 | if (hs2.magic1, hs2.enet, hs2.magic2) != (0x145, 1234567890, 0x14514545): 58 | self.sock.close() 59 | return False 60 | 61 | self.kcp = KcpObj( 62 | hs2.conv, hs2.token, 63 | lambda _, x: self.sock.send(x), 64 | ) 65 | self.kcp.setmtu(1200) 66 | self.kcp.wndsize(1024, 1024) 67 | self.kcp.nodelay(1, 10, 2, 1) 68 | 69 | threading.Thread(target=self._kcp_update).start() 70 | threading.Thread(target=self._kcp_recv).start() 71 | 72 | return True 73 | 74 | def bind(self, addr: _Address) -> bool: 75 | self.sock.bind(addr) 76 | 77 | data, self.addr = self.sock.recvfrom(_BUFFER_SIZE) 78 | hs1 = Handshake.parse(data) 79 | logger.debug('[C] handshake received') 80 | 81 | if (hs1.magic1, hs1.enet, hs1.magic2) != (0xff, 1234567890, 0xffffffff): 82 | self.sock.close() 83 | return False 84 | 85 | conv = random.randrange(1 << 32) 86 | token = random.randrange(1 << 32) 87 | 88 | hs2 = Handshake(0x145, conv, token, 1234567890, 0x14514545) 89 | self.sock.sendto(bytes(hs2), self.addr) 90 | logger.debug('[C] handshake sended') 91 | 92 | self.kcp = KcpObj( 93 | conv, token, 94 | lambda _, x: self.sock.sendto(x, self.addr), 95 | ) 96 | self.kcp.setmtu(1200) 97 | self.kcp.wndsize(1024, 1024) 98 | 99 | threading.Thread(target=self._kcp_update).start() 100 | threading.Thread(target=self._kcp_recv).start() 101 | 102 | return True 103 | 104 | def close(self): 105 | if not self.kcp: 106 | return 107 | 108 | hs = Handshake(0x194, self.kcp.conv, self.kcp.token, 1, 0x19419494) 109 | self.sock.sendto(bytes(hs), self.addr) 110 | self.kcp = None 111 | 112 | self.sock.close() 113 | 114 | def send(self, data: bytes): 115 | self.kcp.send(data) 116 | 117 | def recv(self) -> bytes: 118 | self.recv_queue_semaphore.acquire() 119 | return self.recv_queue.popleft() 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /python-kcp/lkcp/core.pyx: -------------------------------------------------------------------------------- 1 | from cpython.pycapsule cimport * 2 | from libc.stdint cimport uint32_t, int32_t 3 | from cpython.mem cimport PyMem_Malloc, PyMem_Free 4 | from cpython.object cimport PyObject 5 | 6 | cdef extern from "ikcp.h": 7 | ctypedef uint32_t ISTDUINT32; #for linux 8 | ctypedef int32_t ISTDINT32; #for linux 9 | ctypedef ISTDINT32 IINT32; 10 | ctypedef ISTDUINT32 IUINT32; 11 | 12 | struct IQUEUEHEAD: 13 | IQUEUEHEAD *next, *prev 14 | 15 | struct IKCPCB: 16 | IUINT32 conv, token, mtu, mss, state; 17 | IUINT32 snd_una, snd_nxt, rcv_nxt; 18 | IUINT32 ts_recent, ts_lastack, ssthresh; 19 | IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto; 20 | IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe; 21 | IUINT32 current, interval, ts_flush, xmit; 22 | IUINT32 nrcv_buf, nsnd_buf; 23 | IUINT32 nrcv_que, nsnd_que; 24 | IUINT32 nodelay, updated; 25 | IUINT32 ts_probe, probe_wait; 26 | IUINT32 dead_link, incr; 27 | IQUEUEHEAD snd_queue; 28 | IQUEUEHEAD rcv_queue; 29 | IQUEUEHEAD snd_buf; 30 | IQUEUEHEAD rcv_buf; 31 | IUINT32 *acklist; 32 | IUINT32 ackcount; 33 | IUINT32 ackblock; 34 | void *user; 35 | char *buffer; 36 | int fastresend; 37 | int nocwnd; 38 | int logmask; 39 | int (*output)(const char *buf, int len, IKCPCB *kcp, void *user); 40 | void (*writelog)(const char *log, IKCPCB *kcp, void *user); 41 | 42 | ctypedef IKCPCB ikcpcb; 43 | ikcpcb* ikcp_create(IUINT32 conv, IUINT32 token, void *user); 44 | void ikcp_release(ikcpcb *kcp); 45 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len); 46 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len); 47 | void ikcp_update(ikcpcb *kcp, IUINT32 current); 48 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current); 49 | int ikcp_input(ikcpcb *kcp, const char *data, long size); 50 | void ikcp_flush(ikcpcb *kcp); 51 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd); 52 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc); 53 | int ikcp_setmtu(ikcpcb *kcp, int mtu); 54 | 55 | cdef extern from "compat.h": 56 | ctypedef void (*capsule_dest)(PyObject *) 57 | object make_capsule(void *, const char *, capsule_dest) 58 | void* get_pointer(object, const char*) 59 | 60 | cdef struct UsrInfo: 61 | int handle 62 | 63 | g_KcpAgentCbs = {} 64 | 65 | RECV_BUFFER_LEN = 4*1024*1024 66 | 67 | cdef char* recv_buffer = PyMem_Malloc(sizeof(char)*RECV_BUFFER_LEN) 68 | 69 | cdef int kcp_output_callback(const char *buf, int len, ikcpcb *kcp, void *arg): 70 | global g_KcpAgentCbs 71 | cdef UsrInfo *c = arg; 72 | uid = c.handle 73 | cb = g_KcpAgentCbs[uid] 74 | cb(uid, buf[:len]) 75 | return 0 76 | 77 | cdef void del_kcp(PyObject *obj): 78 | cdef ikcpcb* ckcp = get_pointer(obj, NULL) 79 | cdef UsrInfo *c = NULL 80 | if ckcp.user != NULL: 81 | global g_KcpAgentCbs 82 | c = ckcp.user 83 | uid = c.handle 84 | del g_KcpAgentCbs[uid] 85 | PyMem_Free(c) 86 | c = NULL 87 | ckcp.user = NULL 88 | ikcp_release(ckcp) 89 | 90 | def lkcp_create(conv, token, uid, cb): 91 | global g_KcpAgentCbs 92 | g_KcpAgentCbs[uid] = cb 93 | cdef UsrInfo *c = PyMem_Malloc(sizeof(UsrInfo)) 94 | c.handle = uid 95 | cdef ikcpcb* ckcp = ikcp_create(conv, token, c) 96 | ckcp.output = kcp_output_callback 97 | return make_capsule(ckcp, NULL, del_kcp) 98 | 99 | def lkcp_recv(kcp): 100 | global recv_buffer 101 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 102 | hr = ikcp_recv(ckcp, recv_buffer, RECV_BUFFER_LEN) 103 | if hr <= 0: 104 | return hr,None 105 | else: 106 | return hr,recv_buffer[:hr] 107 | 108 | def lkcp_send(kcp, data): 109 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 110 | cdef char* ptr = data 111 | cdef size_t size = len(data) 112 | return ikcp_send(ckcp, ptr, size) 113 | 114 | def lkcp_update(kcp, current): 115 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 116 | cdef int32_t i_cur = current 117 | ikcp_update(ckcp, i_cur) 118 | 119 | def lkcp_check(kcp, current): 120 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 121 | cdef int32_t i_cur = current 122 | return ikcp_check(ckcp, i_cur) 123 | 124 | def lkcp_input(kcp, data): 125 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 126 | cdef char* ptr = data 127 | cdef size_t size = len(data) 128 | return ikcp_input(ckcp, ptr, size) 129 | 130 | def lkcp_flush(kcp): 131 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 132 | ikcp_flush(ckcp) 133 | 134 | def lkcp_wndsize(kcp, sndwnd, rcvwnd): 135 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 136 | cdef int32_t i_nsnd = sndwnd 137 | cdef int32_t i_nrcv = rcvwnd 138 | ikcp_wndsize(ckcp, i_nsnd, i_nrcv) 139 | 140 | def lkcp_nodelay(kcp, nodelay, interval, resend, nc): 141 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 142 | cdef int32_t i_nodelay = nodelay 143 | cdef int32_t i_interval = interval 144 | cdef int32_t i_resend = resend 145 | cdef int32_t i_nc = nc 146 | return ikcp_nodelay(ckcp, i_nodelay, i_interval, i_resend, i_nc) 147 | 148 | def lkcp_setmtu(kcp, mtu): 149 | cdef ikcpcb* ckcp = get_pointer(kcp, NULL) 150 | ikcp_setmtu(ckcp, mtu) -------------------------------------------------------------------------------- /gidra/proxy/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import base64 4 | from enum import Enum 5 | from threading import Thread 6 | from typing import Callable 7 | 8 | from betterproto import Message 9 | from loguru import logger 10 | 11 | from gidra import mhycrypt 12 | from gidra.proxy.cmdids import CmdID 13 | from gidra.proxy.kcp_socket import KcpSocket, _Address 14 | from gidra.proxy.packet import Packet 15 | 16 | Handler = Callable[['GenshinProxy', Message], None] 17 | 18 | 19 | class PacketDirection(Enum): 20 | Client = 0 21 | Server = 1 22 | 23 | 24 | class HandlerRouter: 25 | _handlers: dict[tuple[CmdID, PacketDirection], Handler] 26 | 27 | def __init__(self): 28 | self._handlers = {} 29 | 30 | def add(self, router: HandlerRouter): 31 | self._handlers |= router._handlers 32 | 33 | def get(self, cmdid: CmdID, source: PacketDirection) -> Handler | None: 34 | return self._handlers.get((cmdid, source)) 35 | 36 | def __call__(self, cmdid: CmdID, source: PacketDirection): 37 | def wrapper(handler: Handler): 38 | self._handlers[(cmdid, source)] = handler 39 | return handler 40 | return wrapper 41 | 42 | 43 | class ServerProxy(Thread): 44 | client_proxy: ClientProxy 45 | 46 | def __init__(self, proxy: GenshinProxy, addr: _Address): 47 | self.proxy = proxy 48 | self.proxy_server_addr = addr 49 | self.sock = KcpSocket() 50 | super().__init__(daemon=True) 51 | 52 | def run(self): 53 | if not self.sock.bind(self.proxy_server_addr): 54 | logger.error('[C] can\'t bind') 55 | return 56 | 57 | logger.info('[C] binded') 58 | while True: 59 | data = self.sock.recv() 60 | # logger.debug(f'[C] {data.hex()}') 61 | self.proxy.handle(data, PacketDirection.Server) 62 | 63 | 64 | class ClientProxy(Thread): 65 | server_proxy: ServerProxy 66 | 67 | def __init__(self, proxy: GenshinProxy, addr: _Address): 68 | self.proxy = proxy 69 | self.server_addr = addr 70 | self.sock = KcpSocket() 71 | super().__init__(daemon=True) 72 | 73 | def run(self): 74 | if not self.sock.connect(self.server_addr): 75 | logger.error('[S] can\'t connect') 76 | return 77 | 78 | logger.info('[S] connected') 79 | while True: 80 | data = self.sock.recv() 81 | # logger.debug(f'[S] {data.hex()}') 82 | self.proxy.handle(data, PacketDirection.Client) 83 | 84 | 85 | class GenshinProxy: 86 | def __init__(self, src_addr: _Address, dst_addr: _Address): 87 | self.router = HandlerRouter() 88 | self.src_addr = src_addr 89 | self.dst_addr = dst_addr 90 | 91 | self.key = base64.b64decode('ra2pA3DmSov1sW9yb5NX99EI4NQtxzaRUkQonG86OJuNN0OSnjE/vOV84LpA/U7/cE7a+iXhLzf7atJddeibOL9HVVf2TjGaITgt3/7gmL4IOrC8HrYe6EnSaCzSxZollZ67jGjnwT0ZGJG+IPprBL6X66vFkSrCDodYKw/3ovqBHnnLc0YuDuuCTnuZ6Wuyu0bHG9D64WHQHgmB1X2d9qHQ23pGnoQkhtToBB0xiJYt1Ct0sHx5BcV+5/akhwQ0pADMsGOzkAujEickXrq+67TZjmDAeFV0pJqI7xcdDs6FbQvbjio8SulwcS818f4OTOgWYo0bj0EF+TmZEWHGHCfrwPs6rcwAUiq+ecvrv5KSl95pJE6fXJKGvM6nOr5US4jYi4T1hhCsS3CeXVgG1/8VTf7HIOR9I4hSKOCB8Ijk9f+QT1KxwPZ1DcAwVFF3E05XFEbHU58Cc+xGKKav/XCMvtTTbXw6afPJnldm8+a+q2ELIsTi6UV+M7H6tE8E6byJiZajGURXCrjr2aEBS/zuKyuINvKT01agBmy2tNd6ZWFgzHMfKx+BexTMZ8/hPaDN9n211cZcg5kPqi44L/BwgZpPS3f8JUSSiqjc1vh0ohHQOQZiu4kdJUwyE119gZ2Qn9C+/H1t6v4CSbg30cXxMDN3D3lgsDgTgJECGZVta+obpaJm6ixiwI2Ry9ltuInUE/83WzHweHDy4Y89jHCzhe+l+zhGNxp2epzRfl5HlwzBzQpuv5CF3B88hmOiXC26vIgG7UiisXB7tZ+7uUhcnh3y+zKKtnDuiS5aemNcn0GGHVCSmOlTvFBH/CTjJYLCXeay4656Ie96gcoQZbw9jBYhqvzxYLKR3ptf8v0Kl28LMuAfTs+lemht7XF8JkfY5UH6NUwKLcsOox26KLbuCsyE8e+GFbLxAREGX6+U3zy/aO4wMRLfsQ9PKOyO5+0bfYBsirdXApLwqWmDumhqi+BbBJox2cgINf7zaxWoUZiUhTW1thByH9YXl1vPwjMoh9hmSYkfROPaON0CmI/OyZBSPGVQ4tGFHev+KOEGl542XB0WU5Uq5uE/YH1n3eE2BeuDQT/6NaSfGSULVvaX4/Pq/FjAe1SBNIk06OAj8Y3AjDfoQLeHOc4YhZJc+r2o5EhBFRc+uNkFBVLCgg05Pku+xY8v36Tv8M0lXziSUTFkhd6x3YMaC9F8Yk3F15yYHBYJzfkp4d5T8Y6/YsSUGRVgZQ78Ce+5OehkHT9xukOHdo/GGXc46sNGZRwWZ4KZdjMoo2pxcFot2b4JTLH0YRo0l9ibiuyZxbg9tF9IhC2SJGpFxFlL6MUXL+U5AKfOnMZi6NvIRNDePLMGfzCjngNq3upoARz57FUzFSx2ImgkQ17564mmYT4YtlqaGu8EvlIgkwsBF2eDY1nFia0IM0keLZcAT+M9IbTCNSbnulG4oxYyM0IgErzaITs5teXTNOVSFiXxHn8ykDBsTXcbyQeQX77Os61oibBqtJJNvMd1NB/ZO30aISwpUpd7a+HbFdve682JR07KLfjBK2NHKWtXh33SNfnTqKHCisBbQdHPULRK0hc3W8RhpKu+xyRVubvX46zWK+uzmSNfdAwc6nee77BpafKFBEFzd5aDMs04whCrR1uMBNMcybOpULY62GUa+jAuuHIJVoCAKTAu6tg+PTBzLDsoFXIrvQrHdZIWqSp1h1CmjfQoVVH9CqMTV6XiTyxgywrBM7eKlugcznvVEOkvhi4wmbW3qYkAHX5J6a4cm758surmMPl9vSSU4AaNyBMSXi47svzd3STkh1MYPPXP6UXovzRXV4S48zoTSAPrFmQAitOHgieDSD41c3cJ8wXU1UY13IcWhKxIjMBNGb9m4ESRIU4NyhT4KFQ8aM6O8yR5xnRpbNc9YezygJKlzCEwVhGLMBEr1lWzdk6F+YrAvNdSqlWP9p6hJiuqR4CyfCNQAA5wer0yWdONr6inSM1rgyu3VnQyvPz578f/DthQatPx5ZFdNrwOCR1HG4qBCp225f+zqi1meBIAZGoM/CJuP7jB5MOG/cYpjIwHixw0Hn3QZLmDkfTHlR/Waf/Io7K1nFrp7sIAQXLAgXOdJOUzJanrYITjeyH5aIZsg8beVR1DauytInMlV+34whZ66PQZ5HZqj9LuSBBeBO3FXUmirr0ZHi6nLODCNlZQddjxWQKtQ45pyTXxft/NBPUYw5IQdxUe0zTfBUGdJKtJLWpOxVN9zHDWf3P7AeGXo/mDERRLNby2rFt16hpn21btWmL9a+iQtyzYXON5o85NY+nl2gDaGZcb1anx0153D1Tyi+dvHdHCxm1Sh9XEW7GZOmw8x6HRXmJlHbUMKAhSd2TLJ5QQm2NBE/wNqW3j94Aaih04YGsB1UMwq04qWg+5YHXF5FEEO3GBbYFQLMRjCM0mTjPm1pfjpwaDoplgPPt8ZpOxYQMzIM16zRrLryBkw/8xFyx/WO3wANzZfa9BSVr/hHbTBM2lFW+nYRYlZkWpYXN1OciefhBCZlYXBJ4Drf6+ed3IUnCzRAqUHmW3WDnYixXoeUsB3o/p4WHvljQM8hD5ySnFQNp+FfO0JBlVGYgNodYVzpUm0FAubTkxA3zlcd/bXyKrMPCpDfwhrdGLG+/JvjLZ2ltB5eBK5XzNvSfXB89aPy20tYczccjU+Id1P9S6Ay8aHz0ACchOhkXdWxxc9A5x2pTA8ZiD7GV8/C8cmh4Vy6Nfjr5q7A8a0uplRNEQVEGp3e0DUvVwob2KGbcmigZRrTyMbBMmnqlf0xbhKtuuJ2jwTJFKhAsVOsa7rRYw97DUd/qK2mOR+BwL1oI3x5zJBnwiZhmVl0iTCG+IoWbL0DKWuP4vrFFiqlprglYx88cJODM/pJLh8TZ0d0ogJ6QV0lmbRQ2QKJzBtwQk7SIqJU8fAGEeHhScxHoRw/eT2uA2+bs4QGJlw/a3ShOpF86xhE/d1L9hJGp2aTevCf3oxpr9YYmMtEb96PhAb4RJuOc/XqSonzLquJnv8YUR8nKBSNbXSFfYeylTu+2JFualaXezjgBbwi1vFvtr/sgOxr/IkZrtwpzETRpCt00kgVb7sp1hEmq9RDo6mMqtPl64EdbXkl3LHJqKIk4OA5E5Nbw6/vH4xlXJ/9F1JWSsMc3qxDQkTbDekb7Mql1SP39J9qbELoPgbt9J7+YAB/iLU6hkgVtzzNx4gvIWcgKL3h1OAYTWBDxGVw5x+S/8kOKZMosiK+49JW8I907aHjypGOWqRA+3OyMeaBOgLMmyagB85iv3BDURPgifUFOXJH2UMrvtm86DgSWM0ddHWrtC5rebous56Sq4w9PTvPRgWG34K+9yZY6Nx5n61vO/xy1qpbwqEiSosDwJXkHZWOguGWgnKAsw21IbdmCTf+DqfuaRYF0J+ETNXFN9IMdXmUYcrdlA87YI4NMVXFRvsrwMKGnpDwzwEkdKiWlA6HDGQc20jgoKDAZhLb8Rxo+NRaA5/h8w0DQWvLIRd0iQVVxeWZtHKj61XV6ZprrIHSf4xSScJJddGrK0rz4/QCuDZD1tcLk0Yn+oO7PcE+00FRt7IYDPT/TSE/BNDd7aZPy03TAWuz8fTxPSbqTGL8PxrgyturKxoBW7q68Mn6c7zcN7VVDWdw4pLqXvtSOSFrRC6SgewjMZ1vStJx45ZWKUFu7pPohRrP7+okVGdC26pRd+21yYByJAzP/twS+g2qqSZb8/CdUCb+sbUbgwlvdJOs5CQQILBLnj+D8xJYsTP50t4zP4APN1VFeO1DBJxZzfhdsQFjPJPEvvH/BA+15ZFi9qtS6IdMu7xFbDV26xQbrB2L2jM65mU453felgHP8CkRF8JodNiEWmfDwj6sO452xOX4Q9lVxs+l4ZSx44hEPVDOQvGsW9YEPlZn0/W3NcXtq1OCQzAd5ywIYk9RuX9+bh0RN0thHfFcxo/zAGejtvt8IWPpJqXpoyg37NFcJAKN76Nm+7qNmFF8v3GOh5CFBCYd03EWCdquizBbRgkldXogRwJlYmZ/F1D711Z9jS0jbZEbiy0/kQxSMNeHiz1NSRcccIHD/bwk3G4XFG5j2PHuCK5X0GxGvvbKZlmsWv1O09ZwoIZ2H06ct0qUVz4wup3MU+L/px81xROI7clb89xpYtfABRh3NLB/6omhvEeIQI65cF6r3GYmm4bgRSpCdbCgGZPDMV3xEcW3J4GwL/0BkyoTmDymKq5gEjsk8w6jopVkWPUiwmNuZ7jl9BuNKLsjHSNSxFfmuy81opCneV3G6Rsqg/SO9Vqp3zcVwNxRWD/XDrI28N8Tca+9bimeGMlmrExGwHrtplGx0EcGhO3K9TqHS2atPEogvUm0G/E6I9/KwatkDWirRtDQIk3HCsX+1enUaUsyUwXL1IprFxUlyLDu0CsJkKhVILAvmITiQZbjWEHyr+EkXA/H9r1N98qIRg4BiAJ9vQlPe9lPYzsiW9sEPFhDHfSxksZosGx0z2w2BVYKInM5hyO6U357wvX4vkwXm6NbIhpbPQVRdionHqs/gtFJ3vkj6mmdZn65i1SdYFhHAv3M/dauWI8oCoc9Io052ifJbAWTlAhFHGPyDI0m+gy4AzR7/Eeo6gh3qXhNc4BAJ0a/FKOohgbZ6Cb7c032RN0eChPCLfTDmI+hReMmx3yx8OvNDloaU1d1AC+4+DeTYxymiG7c9WddYTnYHV5d5wDuBkYRlQBdoJJKhY5Q4Ksz4HfD4W4o9tJ0sPIrgVd3xj7PtFo4lcD8daDPesf3U0wonWPWPQ1zh/aqiQaVQFAbMUzkYT/poPmAJT7/jbRWkiDStScm9CkVLkFFHjPUDNT4474h6USYa3CGgwfLRB/XYLLZZDfuxxk3II2rpdBYtPwR2BNUhCN9KO4OSDP0aL6UHVET9R+QWYXv1wJqGUqmtoTO6wtbXFSgMLWC8iU+QWcBVIYKhXTHS0c3sCpzwW0DKyZvZn3PLS5ckETl5Hk76inZOHFq/GM2vDoiudBcLud3nJc9+jYLpettbr5TpJPGdz/VY2xEboKSorSiXEHnoAeXYPU3t4FRDycXAiLqVJ4I0UApTHPQddTNJoPa1XFknjEZTOVyJ5tUgl6lUG02Y2xNomesD0A9drtDHp1JvvSLLKbwSETzisq7xJHrp0FFJNaz1a5CdWwOwKcjJOVipclHifT525ltbThgPod8bM2u0GMqQhHnF3smOryeeNlFNc8btKqh+KolA8NUFxg5giRQDHhxnjo3L4lTujGP9Q2XBYZpB806ZK9PJpjiuzQx02C/H05aT05jXGW5BfklrHjwOWUKl+NyinF7ZvyknqzAISpdkJ/1BT2HqL7kLX4e3fjFHy1hV7GXtzLjhvDtHgUNGO9eITPQLkNvNSKDpfH8f3kGSJ9jwvu8LdSIbc6mk9H0xwIR1W13GcmA==') 92 | self.client_seed = 0 93 | self.uid = 0 94 | 95 | def add(self, router: HandlerRouter): 96 | self.router.add(router) 97 | 98 | def handle(self, data: bytes, direction: PacketDirection): 99 | data = mhycrypt.xor(data, self.key) 100 | try: 101 | packet = Packet().parse(data) 102 | except Exception: 103 | logger.debug(f'{PacketDirection(direction.value ^ 1).name} -> {direction.name}: {data.hex()}') 104 | self.send_raw(data, direction) 105 | return 106 | 107 | logger.debug(f'{PacketDirection(direction.value ^ 1).name} -> {direction.name} {packet.cmdid}: {packet.body.__class__.__name__}') 108 | 109 | if handler := self.router.get(packet.cmdid, PacketDirection(direction.value ^ 1)): 110 | handler(self, packet.body) 111 | else: 112 | self.send_raw(data, direction) 113 | 114 | def send(self, msg: Message, direction: PacketDirection): 115 | packet = Packet(body=msg) 116 | self.send_raw(bytes(packet), direction) 117 | 118 | def send_raw(self, data: bytes, direction: PacketDirection): 119 | data = mhycrypt.xor(data, self.key) 120 | 121 | match direction: 122 | case PacketDirection.Server: self.client_proxy.sock.send(data) 123 | case PacketDirection.Client: self.server_proxy.sock.send(data) 124 | 125 | def start(self): 126 | self.server_proxy = ServerProxy(self, self.src_addr) 127 | self.client_proxy = ClientProxy(self, self.dst_addr) 128 | 129 | self.server_proxy.client_proxy = self.client_proxy 130 | self.client_proxy.server_proxy = self.server_proxy 131 | 132 | self.server_proxy.start() 133 | self.client_proxy.start() 134 | -------------------------------------------------------------------------------- /python-kcp/lkcp/ikcp.h: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // KCP - A Better ARQ Protocol Implementation 4 | // skywind3000 (at) gmail.com, 2010-2011 5 | // 6 | // Features: 7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 8 | // + Maximum RTT reduce three times vs tcp. 9 | // + Lightweight, distributed as a single source file. 10 | // 11 | //===================================================================== 12 | #ifndef __IKCP_H__ 13 | #define __IKCP_H__ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | //===================================================================== 21 | // 32BIT INTEGER DEFINITION 22 | //===================================================================== 23 | #ifndef __INTEGER_32_BITS__ 24 | #define __INTEGER_32_BITS__ 25 | #if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \ 26 | defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \ 27 | defined(_M_AMD64) 28 | typedef unsigned int ISTDUINT32; 29 | typedef int ISTDINT32; 30 | #elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \ 31 | defined(__i386) || defined(_M_X86) 32 | typedef unsigned long ISTDUINT32; 33 | typedef long ISTDINT32; 34 | #elif defined(__MACOS__) 35 | typedef UInt32 ISTDUINT32; 36 | typedef SInt32 ISTDINT32; 37 | #elif defined(__APPLE__) && defined(__MACH__) 38 | #include 39 | typedef u_int32_t ISTDUINT32; 40 | typedef int32_t ISTDINT32; 41 | #elif defined(__BEOS__) 42 | #include 43 | typedef u_int32_t ISTDUINT32; 44 | typedef int32_t ISTDINT32; 45 | #elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__)) 46 | typedef unsigned __int32 ISTDUINT32; 47 | typedef __int32 ISTDINT32; 48 | #elif defined(__GNUC__) 49 | #include 50 | typedef uint32_t ISTDUINT32; 51 | typedef int32_t ISTDINT32; 52 | #else 53 | typedef unsigned long ISTDUINT32; 54 | typedef long ISTDINT32; 55 | #endif 56 | #endif 57 | 58 | 59 | //===================================================================== 60 | // Integer Definition 61 | //===================================================================== 62 | #ifndef __IINT8_DEFINED 63 | #define __IINT8_DEFINED 64 | typedef char IINT8; 65 | #endif 66 | 67 | #ifndef __IUINT8_DEFINED 68 | #define __IUINT8_DEFINED 69 | typedef unsigned char IUINT8; 70 | #endif 71 | 72 | #ifndef __IUINT16_DEFINED 73 | #define __IUINT16_DEFINED 74 | typedef unsigned short IUINT16; 75 | #endif 76 | 77 | #ifndef __IINT16_DEFINED 78 | #define __IINT16_DEFINED 79 | typedef short IINT16; 80 | #endif 81 | 82 | #ifndef __IINT32_DEFINED 83 | #define __IINT32_DEFINED 84 | typedef ISTDINT32 IINT32; 85 | #endif 86 | 87 | #ifndef __IUINT32_DEFINED 88 | #define __IUINT32_DEFINED 89 | typedef ISTDUINT32 IUINT32; 90 | #endif 91 | 92 | #ifndef __IINT64_DEFINED 93 | #define __IINT64_DEFINED 94 | #if defined(_MSC_VER) || defined(__BORLANDC__) 95 | typedef __int64 IINT64; 96 | #else 97 | typedef long long IINT64; 98 | #endif 99 | #endif 100 | 101 | #ifndef __IUINT64_DEFINED 102 | #define __IUINT64_DEFINED 103 | #if defined(_MSC_VER) || defined(__BORLANDC__) 104 | typedef unsigned __int64 IUINT64; 105 | #else 106 | typedef unsigned long long IUINT64; 107 | #endif 108 | #endif 109 | 110 | #ifndef INLINE 111 | #if defined(__GNUC__) 112 | 113 | #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) 114 | #define INLINE __inline__ __attribute__((always_inline)) 115 | #else 116 | #define INLINE __inline__ 117 | #endif 118 | 119 | #elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__)) 120 | #define INLINE __inline 121 | #else 122 | #define INLINE 123 | #endif 124 | #endif 125 | 126 | #ifndef inline 127 | #define inline INLINE 128 | #endif 129 | 130 | 131 | //===================================================================== 132 | // QUEUE DEFINITION 133 | //===================================================================== 134 | #ifndef __IQUEUE_DEF__ 135 | #define __IQUEUE_DEF__ 136 | 137 | struct IQUEUEHEAD { 138 | struct IQUEUEHEAD *next, *prev; 139 | }; 140 | 141 | typedef struct IQUEUEHEAD iqueue_head; 142 | 143 | 144 | //--------------------------------------------------------------------- 145 | // queue init 146 | //--------------------------------------------------------------------- 147 | #define IQUEUE_HEAD_INIT(name) { &(name), &(name) } 148 | #define IQUEUE_HEAD(name) \ 149 | struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name) 150 | 151 | #define IQUEUE_INIT(ptr) ( \ 152 | (ptr)->next = (ptr), (ptr)->prev = (ptr)) 153 | 154 | #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 155 | 156 | #define ICONTAINEROF(ptr, type, member) ( \ 157 | (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) ) 158 | 159 | #define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member) 160 | 161 | 162 | //--------------------------------------------------------------------- 163 | // queue operation 164 | //--------------------------------------------------------------------- 165 | #define IQUEUE_ADD(node, head) ( \ 166 | (node)->prev = (head), (node)->next = (head)->next, \ 167 | (head)->next->prev = (node), (head)->next = (node)) 168 | 169 | #define IQUEUE_ADD_TAIL(node, head) ( \ 170 | (node)->prev = (head)->prev, (node)->next = (head), \ 171 | (head)->prev->next = (node), (head)->prev = (node)) 172 | 173 | #define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n)) 174 | 175 | #define IQUEUE_DEL(entry) (\ 176 | (entry)->next->prev = (entry)->prev, \ 177 | (entry)->prev->next = (entry)->next, \ 178 | (entry)->next = 0, (entry)->prev = 0) 179 | 180 | #define IQUEUE_DEL_INIT(entry) do { \ 181 | IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0) 182 | 183 | #define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next) 184 | 185 | #define iqueue_init IQUEUE_INIT 186 | #define iqueue_entry IQUEUE_ENTRY 187 | #define iqueue_add IQUEUE_ADD 188 | #define iqueue_add_tail IQUEUE_ADD_TAIL 189 | #define iqueue_del IQUEUE_DEL 190 | #define iqueue_del_init IQUEUE_DEL_INIT 191 | #define iqueue_is_empty IQUEUE_IS_EMPTY 192 | 193 | #define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \ 194 | for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \ 195 | &((iterator)->MEMBER) != (head); \ 196 | (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER)) 197 | 198 | #define iqueue_foreach(iterator, head, TYPE, MEMBER) \ 199 | IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) 200 | 201 | #define iqueue_foreach_entry(pos, head) \ 202 | for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next ) 203 | 204 | 205 | #define __iqueue_splice(list, head) do { \ 206 | iqueue_head *first = (list)->next, *last = (list)->prev; \ 207 | iqueue_head *at = (head)->next; \ 208 | (first)->prev = (head), (head)->next = (first); \ 209 | (last)->next = (at), (at)->prev = (last); } while (0) 210 | 211 | #define iqueue_splice(list, head) do { \ 212 | if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0) 213 | 214 | #define iqueue_splice_init(list, head) do { \ 215 | iqueue_splice(list, head); iqueue_init(list); } while (0) 216 | 217 | 218 | #ifdef _MSC_VER 219 | #pragma warning(disable:4311) 220 | #pragma warning(disable:4312) 221 | #pragma warning(disable:4996) 222 | #endif 223 | 224 | #endif 225 | 226 | 227 | //--------------------------------------------------------------------- 228 | // WORD ORDER 229 | //--------------------------------------------------------------------- 230 | #ifndef IWORDS_BIG_ENDIAN 231 | #ifdef _BIG_ENDIAN_ 232 | #if _BIG_ENDIAN_ 233 | #define IWORDS_BIG_ENDIAN 1 234 | #endif 235 | #endif 236 | #ifndef IWORDS_BIG_ENDIAN 237 | #if defined(__hppa__) || \ 238 | defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ 239 | (defined(__MIPS__) && defined(__MISPEB__)) || \ 240 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ 241 | defined(__sparc__) || defined(__powerpc__) || \ 242 | defined(__mc68000__) || defined(__s390x__) || defined(__s390__) 243 | #define IWORDS_BIG_ENDIAN 1 244 | #endif 245 | #endif 246 | #ifndef IWORDS_BIG_ENDIAN 247 | #define IWORDS_BIG_ENDIAN 0 248 | #endif 249 | #endif 250 | 251 | 252 | 253 | //===================================================================== 254 | // SEGMENT 255 | //===================================================================== 256 | struct IKCPSEG 257 | { 258 | struct IQUEUEHEAD node; 259 | IUINT32 conv; 260 | IUINT32 token; 261 | IUINT32 cmd; 262 | IUINT32 frg; 263 | IUINT32 wnd; 264 | IUINT32 ts; 265 | IUINT32 sn; 266 | IUINT32 una; 267 | IUINT32 len; 268 | IUINT32 resendts; 269 | IUINT32 rto; 270 | IUINT32 fastack; 271 | IUINT32 xmit; 272 | char data[1]; 273 | }; 274 | 275 | 276 | //--------------------------------------------------------------------- 277 | // IKCPCB 278 | //--------------------------------------------------------------------- 279 | struct IKCPCB 280 | { 281 | IUINT32 conv, token, mtu, mss, state; 282 | IUINT32 snd_una, snd_nxt, rcv_nxt; 283 | IUINT32 ts_recent, ts_lastack, ssthresh; 284 | IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto; 285 | IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe; 286 | IUINT32 current, interval, ts_flush, xmit; 287 | IUINT32 nrcv_buf, nsnd_buf; 288 | IUINT32 nrcv_que, nsnd_que; 289 | IUINT32 nodelay, updated; 290 | IUINT32 ts_probe, probe_wait; 291 | IUINT32 dead_link, incr; 292 | struct IQUEUEHEAD snd_queue; 293 | struct IQUEUEHEAD rcv_queue; 294 | struct IQUEUEHEAD snd_buf; 295 | struct IQUEUEHEAD rcv_buf; 296 | IUINT32 *acklist; 297 | IUINT32 ackcount; 298 | IUINT32 ackblock; 299 | void *user; 300 | char *buffer; 301 | int fastresend; 302 | int nocwnd; 303 | int logmask; 304 | int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user); 305 | void (*writelog)(const char *log, struct IKCPCB *kcp, void *user); 306 | }; 307 | 308 | 309 | typedef struct IKCPCB ikcpcb; 310 | 311 | #define IKCP_LOG_OUTPUT 1 312 | #define IKCP_LOG_INPUT 2 313 | #define IKCP_LOG_SEND 4 314 | #define IKCP_LOG_RECV 8 315 | #define IKCP_LOG_IN_DATA 16 316 | #define IKCP_LOG_IN_ACK 32 317 | #define IKCP_LOG_IN_PROBE 64 318 | #define IKCP_LOG_IN_WINS 128 319 | #define IKCP_LOG_OUT_DATA 256 320 | #define IKCP_LOG_OUT_ACK 512 321 | #define IKCP_LOG_OUT_PROBE 1024 322 | #define IKCP_LOG_OUT_WINS 2048 323 | 324 | #ifdef __cplusplus 325 | extern "C" { 326 | #endif 327 | 328 | //--------------------------------------------------------------------- 329 | // interface 330 | //--------------------------------------------------------------------- 331 | 332 | // create a new kcp control object, 'conv' and 'token' must equal in two endpoint 333 | // from the same connection. 'user' will be passed to the output callback 334 | // output callback can be setup like this: 'kcp->output = my_udp_output' 335 | ikcpcb* ikcp_create(IUINT32 conv, IUINT32 token, void *user); 336 | 337 | // release kcp control object 338 | void ikcp_release(ikcpcb *kcp); 339 | 340 | // user/upper level recv: returns size, returns below zero for EAGAIN 341 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len); 342 | 343 | // user/upper level send, returns below zero for error 344 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len); 345 | 346 | // update state (call it repeatedly, every 10ms-100ms), or you can ask 347 | // ikcp_check when to call it again (without ikcp_input/_send calling). 348 | // 'current' - current timestamp in millisec. 349 | void ikcp_update(ikcpcb *kcp, IUINT32 current); 350 | 351 | // Determine when should you invoke ikcp_update: 352 | // returns when you should invoke ikcp_update in millisec, if there 353 | // is no ikcp_input/_send calling. you can call ikcp_update in that 354 | // time, instead of call update repeatly. 355 | // Important to reduce unnacessary ikcp_update invoking. use it to 356 | // schedule ikcp_update (eg. implementing an epoll-like mechanism, 357 | // or optimize ikcp_update when handling massive kcp connections) 358 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current); 359 | 360 | // when you received a low level packet (eg. UDP packet), call it 361 | int ikcp_input(ikcpcb *kcp, const char *data, long size); 362 | 363 | // flush pending data 364 | void ikcp_flush(ikcpcb *kcp); 365 | 366 | // check the size of next message in the recv queue 367 | int ikcp_peeksize(const ikcpcb *kcp); 368 | 369 | // change MTU size, default is 1400 370 | int ikcp_setmtu(ikcpcb *kcp, int mtu); 371 | 372 | // set maximum window size: sndwnd=32, rcvwnd=32 by default 373 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd); 374 | 375 | // get how many packet is waiting to be sent 376 | int ikcp_waitsnd(const ikcpcb *kcp); 377 | 378 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) 379 | // nodelay: 0:disable(default), 1:enable 380 | // interval: internal update timer interval in millisec, default is 100ms 381 | // resend: 0:disable fast resend(default), 1:enable fast resend 382 | // nc: 0:normal congestion control(default), 1:disable congestion control 383 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc); 384 | 385 | int ikcp_rcvbuf_count(const ikcpcb *kcp); 386 | int ikcp_sndbuf_count(const ikcpcb *kcp); 387 | 388 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...); 389 | 390 | // setup allocator 391 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)); 392 | 393 | 394 | #ifdef __cplusplus 395 | } 396 | #endif 397 | 398 | #endif 399 | 400 | 401 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "betterproto" 3 | version = "2.0.0b4" 4 | description = "A better Protobuf / gRPC generator & library" 5 | category = "main" 6 | optional = false 7 | python-versions = ">=3.6.2,<4.0" 8 | 9 | [package.dependencies] 10 | grpclib = ">=0.4.1,<0.5.0" 11 | python-dateutil = ">=2.8,<3.0" 12 | 13 | [package.extras] 14 | compiler = ["black (>=19.3b0)", "jinja2 (>=2.11.2,<3.0.0)"] 15 | 16 | [[package]] 17 | name = "bottle" 18 | version = "0.12.21" 19 | description = "Fast and simple WSGI-framework for small web-applications." 20 | category = "main" 21 | optional = false 22 | python-versions = "*" 23 | 24 | [[package]] 25 | name = "certifi" 26 | version = "2022.6.15" 27 | description = "Python package for providing Mozilla's CA Bundle." 28 | category = "main" 29 | optional = false 30 | python-versions = ">=3.6" 31 | 32 | [[package]] 33 | name = "charset-normalizer" 34 | version = "2.1.0" 35 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 36 | category = "main" 37 | optional = false 38 | python-versions = ">=3.6.0" 39 | 40 | [package.extras] 41 | unicode_backport = ["unicodedata2"] 42 | 43 | [[package]] 44 | name = "colorama" 45 | version = "0.4.4" 46 | description = "Cross-platform colored terminal text." 47 | category = "main" 48 | optional = false 49 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 50 | 51 | [[package]] 52 | name = "ec2b" 53 | version = "1.0.0" 54 | description = "Python bindings for Ec2b" 55 | category = "main" 56 | optional = false 57 | python-versions = "*" 58 | develop = false 59 | 60 | [package.source] 61 | type = "git" 62 | url = "https://github.com/GrownNed/ec2b.py" 63 | reference = "master" 64 | resolved_reference = "aafdd4c162e874353c05c067eee169faa1314f5b" 65 | 66 | [[package]] 67 | name = "grpclib" 68 | version = "0.4.2" 69 | description = "Pure-Python gRPC implementation for asyncio" 70 | category = "main" 71 | optional = false 72 | python-versions = ">=3.6" 73 | 74 | [package.dependencies] 75 | h2 = ">=3.1.0,<5" 76 | multidict = "*" 77 | 78 | [[package]] 79 | name = "h2" 80 | version = "4.1.0" 81 | description = "HTTP/2 State-Machine based protocol implementation" 82 | category = "main" 83 | optional = false 84 | python-versions = ">=3.6.1" 85 | 86 | [package.dependencies] 87 | hpack = ">=4.0,<5" 88 | hyperframe = ">=6.0,<7" 89 | 90 | [[package]] 91 | name = "hpack" 92 | version = "4.0.0" 93 | description = "Pure-Python HPACK header compression" 94 | category = "main" 95 | optional = false 96 | python-versions = ">=3.6.1" 97 | 98 | [[package]] 99 | name = "hyperframe" 100 | version = "6.0.1" 101 | description = "HTTP/2 framing layer for Python" 102 | category = "main" 103 | optional = false 104 | python-versions = ">=3.6.1" 105 | 106 | [[package]] 107 | name = "idna" 108 | version = "3.3" 109 | description = "Internationalized Domain Names in Applications (IDNA)" 110 | category = "main" 111 | optional = false 112 | python-versions = ">=3.5" 113 | 114 | [[package]] 115 | name = "loguru" 116 | version = "0.6.0" 117 | description = "Python logging made (stupidly) simple" 118 | category = "main" 119 | optional = false 120 | python-versions = ">=3.5" 121 | 122 | [package.dependencies] 123 | colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} 124 | win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} 125 | 126 | [package.extras] 127 | dev = ["colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "black (>=19.10b0)", "isort (>=5.1.1)", "Sphinx (>=4.1.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)"] 128 | 129 | [[package]] 130 | name = "multidict" 131 | version = "6.0.2" 132 | description = "multidict implementation" 133 | category = "main" 134 | optional = false 135 | python-versions = ">=3.7" 136 | 137 | [[package]] 138 | name = "pycryptodome" 139 | version = "3.15.0" 140 | description = "Cryptographic library for Python" 141 | category = "main" 142 | optional = false 143 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 144 | 145 | [[package]] 146 | name = "python-dateutil" 147 | version = "2.8.2" 148 | description = "Extensions to the standard Python datetime module" 149 | category = "main" 150 | optional = false 151 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 152 | 153 | [package.dependencies] 154 | six = ">=1.5" 155 | 156 | [[package]] 157 | name = "requests" 158 | version = "2.28.1" 159 | description = "Python HTTP for Humans." 160 | category = "main" 161 | optional = false 162 | python-versions = ">=3.7, <4" 163 | 164 | [package.dependencies] 165 | certifi = ">=2017.4.17" 166 | charset-normalizer = ">=2,<3" 167 | idna = ">=2.5,<4" 168 | urllib3 = ">=1.21.1,<1.27" 169 | 170 | [package.extras] 171 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 172 | use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] 173 | 174 | [[package]] 175 | name = "six" 176 | version = "1.16.0" 177 | description = "Python 2 and 3 compatibility utilities" 178 | category = "main" 179 | optional = false 180 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 181 | 182 | [[package]] 183 | name = "urllib3" 184 | version = "1.26.9" 185 | description = "HTTP library with thread-safe connection pooling, file post, and more." 186 | category = "main" 187 | optional = false 188 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 189 | 190 | [package.extras] 191 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] 192 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 193 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 194 | 195 | [[package]] 196 | name = "win32-setctime" 197 | version = "1.1.0" 198 | description = "A small Python utility to set file creation time on Windows" 199 | category = "main" 200 | optional = false 201 | python-versions = ">=3.5" 202 | 203 | [package.extras] 204 | dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] 205 | 206 | [metadata] 207 | lock-version = "1.1" 208 | python-versions = "^3.10" 209 | content-hash = "e89451c543a875f1bcc0808e726e3c238350ac8414a710225baf46c26a4b8240" 210 | 211 | [metadata.files] 212 | betterproto = [ 213 | {file = "betterproto-2.0.0b4-py3-none-any.whl", hash = "sha256:6b807038df17a7896cc1f98b42f64eed24c2f350b6d10b2854501f8b9b7d3d1e"}, 214 | {file = "betterproto-2.0.0b4.tar.gz", hash = "sha256:99bc6f866fe9c30100fe438662439205f35bc0e65e4e736c46a6ebfea02c3e7b"}, 215 | ] 216 | bottle = [ 217 | {file = "bottle-0.12.21-py3-none-any.whl", hash = "sha256:6e1c9817019dae3a8c20adacaf09035251798d2ae2fcc8ce43157ee72965f257"}, 218 | {file = "bottle-0.12.21.tar.gz", hash = "sha256:787c61b6cc02b9c229bf2663011fac53dd8fc197f7f8ad2eeede29d888d7887e"}, 219 | ] 220 | certifi = [] 221 | charset-normalizer = [] 222 | colorama = [ 223 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 224 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 225 | ] 226 | ec2b = [] 227 | grpclib = [ 228 | {file = "grpclib-0.4.2.tar.gz", hash = "sha256:ead080cb7d56d6a5e835aaf5255d1ef1dce475a7722566ea225f0188fce33b68"}, 229 | ] 230 | h2 = [ 231 | {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, 232 | {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, 233 | ] 234 | hpack = [ 235 | {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, 236 | {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, 237 | ] 238 | hyperframe = [ 239 | {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, 240 | {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, 241 | ] 242 | idna = [] 243 | loguru = [ 244 | {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, 245 | {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, 246 | ] 247 | multidict = [ 248 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, 249 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, 250 | {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, 251 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, 252 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, 253 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, 254 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, 255 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, 256 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, 257 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, 258 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, 259 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, 260 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, 261 | {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, 262 | {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, 263 | {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, 264 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, 265 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, 266 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, 267 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, 268 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, 269 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, 270 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, 271 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, 272 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, 273 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, 274 | {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, 275 | {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, 276 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, 277 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, 278 | {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, 279 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, 280 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, 281 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, 282 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, 283 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, 284 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, 285 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, 286 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, 287 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, 288 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, 289 | {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, 290 | {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, 291 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, 292 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, 293 | {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, 294 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, 295 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, 296 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, 297 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, 298 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, 299 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, 300 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, 301 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, 302 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, 303 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, 304 | {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, 305 | {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, 306 | {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, 307 | ] 308 | pycryptodome = [] 309 | python-dateutil = [ 310 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 311 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 312 | ] 313 | requests = [] 314 | six = [ 315 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 316 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 317 | ] 318 | urllib3 = [] 319 | win32-setctime = [ 320 | {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, 321 | {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, 322 | ] 323 | -------------------------------------------------------------------------------- /python-kcp/lkcp/ikcp.c: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // KCP - A Better ARQ Protocol Implementation 4 | // skywind3000 (at) gmail.com, 2010-2011 5 | // 6 | // Features: 7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 8 | // + Maximum RTT reduce three times vs tcp. 9 | // + Lightweight, distributed as a single source file. 10 | // 11 | //===================================================================== 12 | #include "ikcp.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | 22 | 23 | //===================================================================== 24 | // KCP BASIC 25 | //===================================================================== 26 | const IUINT32 IKCP_RTO_NDL = 30; // no delay min rto 27 | const IUINT32 IKCP_RTO_MIN = 100; // normal min rto 28 | const IUINT32 IKCP_RTO_DEF = 200; 29 | const IUINT32 IKCP_RTO_MAX = 60000; 30 | const IUINT32 IKCP_CMD_PUSH = 81; // cmd: push data 31 | const IUINT32 IKCP_CMD_ACK = 82; // cmd: ack 32 | const IUINT32 IKCP_CMD_WASK = 83; // cmd: window probe (ask) 33 | const IUINT32 IKCP_CMD_WINS = 84; // cmd: window size (tell) 34 | const IUINT32 IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK 35 | const IUINT32 IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS 36 | const IUINT32 IKCP_WND_SND = 32; 37 | const IUINT32 IKCP_WND_RCV = 32; 38 | const IUINT32 IKCP_MTU_DEF = 1400; 39 | const IUINT32 IKCP_ACK_FAST = 3; 40 | const IUINT32 IKCP_INTERVAL = 100; 41 | const IUINT32 IKCP_OVERHEAD = 28; 42 | const IUINT32 IKCP_DEADLINK = 10; 43 | const IUINT32 IKCP_THRESH_INIT = 2; 44 | const IUINT32 IKCP_THRESH_MIN = 2; 45 | const IUINT32 IKCP_PROBE_INIT = 7000; // 7 secs to probe window size 46 | const IUINT32 IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window 47 | 48 | 49 | //--------------------------------------------------------------------- 50 | // encode / decode 51 | //--------------------------------------------------------------------- 52 | 53 | /* encode 8 bits unsigned int */ 54 | static inline char *ikcp_encode8u(char *p, unsigned char c) 55 | { 56 | *(unsigned char*)p++ = c; 57 | return p; 58 | } 59 | 60 | /* decode 8 bits unsigned int */ 61 | static inline const char *ikcp_decode8u(const char *p, unsigned char *c) 62 | { 63 | *c = *(unsigned char*)p++; 64 | return p; 65 | } 66 | 67 | /* encode 16 bits unsigned int (lsb) */ 68 | static inline char *ikcp_encode16u(char *p, unsigned short w) 69 | { 70 | #if IWORDS_BIG_ENDIAN 71 | *(unsigned char*)(p + 0) = (w & 255); 72 | *(unsigned char*)(p + 1) = (w >> 8); 73 | #else 74 | *(unsigned short*)(p) = w; 75 | #endif 76 | p += 2; 77 | return p; 78 | } 79 | 80 | /* decode 16 bits unsigned int (lsb) */ 81 | static inline const char *ikcp_decode16u(const char *p, unsigned short *w) 82 | { 83 | #if IWORDS_BIG_ENDIAN 84 | *w = *(const unsigned char*)(p + 1); 85 | *w = *(const unsigned char*)(p + 0) + (*w << 8); 86 | #else 87 | *w = *(const unsigned short*)p; 88 | #endif 89 | p += 2; 90 | return p; 91 | } 92 | 93 | /* encode 32 bits unsigned int (lsb) */ 94 | static inline char *ikcp_encode32u(char *p, IUINT32 l) 95 | { 96 | #if IWORDS_BIG_ENDIAN 97 | *(unsigned char*)(p + 0) = (unsigned char)((l >> 0) & 0xff); 98 | *(unsigned char*)(p + 1) = (unsigned char)((l >> 8) & 0xff); 99 | *(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff); 100 | *(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff); 101 | #else 102 | *(IUINT32*)p = l; 103 | #endif 104 | p += 4; 105 | return p; 106 | } 107 | 108 | /* decode 32 bits unsigned int (lsb) */ 109 | static inline const char *ikcp_decode32u(const char *p, IUINT32 *l) 110 | { 111 | #if IWORDS_BIG_ENDIAN 112 | *l = *(const unsigned char*)(p + 3); 113 | *l = *(const unsigned char*)(p + 2) + (*l << 8); 114 | *l = *(const unsigned char*)(p + 1) + (*l << 8); 115 | *l = *(const unsigned char*)(p + 0) + (*l << 8); 116 | #else 117 | *l = *(const IUINT32*)p; 118 | #endif 119 | p += 4; 120 | return p; 121 | } 122 | 123 | static inline IUINT32 _imin_(IUINT32 a, IUINT32 b) { 124 | return a <= b ? a : b; 125 | } 126 | 127 | static inline IUINT32 _imax_(IUINT32 a, IUINT32 b) { 128 | return a >= b ? a : b; 129 | } 130 | 131 | static inline IUINT32 _ibound_(IUINT32 lower, IUINT32 middle, IUINT32 upper) 132 | { 133 | return _imin_(_imax_(lower, middle), upper); 134 | } 135 | 136 | static inline long _itimediff(IUINT32 later, IUINT32 earlier) 137 | { 138 | return ((IINT32)(later - earlier)); 139 | } 140 | 141 | //--------------------------------------------------------------------- 142 | // manage segment 143 | //--------------------------------------------------------------------- 144 | typedef struct IKCPSEG IKCPSEG; 145 | 146 | static void* (*ikcp_malloc_hook)(size_t) = NULL; 147 | static void (*ikcp_free_hook)(void *) = NULL; 148 | 149 | // internal malloc 150 | static void* ikcp_malloc(size_t size) { 151 | if (ikcp_malloc_hook) 152 | return ikcp_malloc_hook(size); 153 | return malloc(size); 154 | } 155 | 156 | // internal free 157 | static void ikcp_free(void *ptr) { 158 | if (ikcp_free_hook) { 159 | ikcp_free_hook(ptr); 160 | } else { 161 | free(ptr); 162 | } 163 | } 164 | 165 | // redefine allocator 166 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)) 167 | { 168 | ikcp_malloc_hook = new_malloc; 169 | ikcp_free_hook = new_free; 170 | } 171 | 172 | // allocate a new kcp segment 173 | static IKCPSEG* ikcp_segment_new(ikcpcb *kcp, int size) 174 | { 175 | return (IKCPSEG*)ikcp_malloc(sizeof(IKCPSEG) + size); 176 | } 177 | 178 | // delete a segment 179 | static void ikcp_segment_delete(ikcpcb *kcp, IKCPSEG *seg) 180 | { 181 | ikcp_free(seg); 182 | } 183 | 184 | // write log 185 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...) 186 | { 187 | char buffer[1024]; 188 | va_list argptr; 189 | if ((mask & kcp->logmask) == 0 || kcp->writelog == 0) return; 190 | va_start(argptr, fmt); 191 | vsprintf(buffer, fmt, argptr); 192 | va_end(argptr); 193 | kcp->writelog(buffer, kcp, kcp->user); 194 | } 195 | 196 | // check log mask 197 | static int ikcp_canlog(const ikcpcb *kcp, int mask) 198 | { 199 | if ((mask & kcp->logmask) == 0 || kcp->writelog == NULL) return 0; 200 | return 1; 201 | } 202 | 203 | // output segment 204 | static int ikcp_output(ikcpcb *kcp, const void *data, int size) 205 | { 206 | assert(kcp); 207 | assert(kcp->output); 208 | if (ikcp_canlog(kcp, IKCP_LOG_OUTPUT)) { 209 | ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", (long)size); 210 | } 211 | if (size == 0) return 0; 212 | return kcp->output((const char*)data, size, kcp, kcp->user); 213 | } 214 | 215 | // output queue 216 | void ikcp_qprint(const char *name, const struct IQUEUEHEAD *head) 217 | { 218 | #if 0 219 | const struct IQUEUEHEAD *p; 220 | printf("<%s>: [", name); 221 | for (p = head->next; p != head; p = p->next) { 222 | const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); 223 | printf("(%lu %d)", (unsigned long)seg->sn, (int)(seg->ts % 10000)); 224 | if (p->next != head) printf(","); 225 | } 226 | printf("]\n"); 227 | #endif 228 | } 229 | 230 | 231 | //--------------------------------------------------------------------- 232 | // create a new kcpcb 233 | //--------------------------------------------------------------------- 234 | ikcpcb* ikcp_create(IUINT32 conv, IUINT32 token, void *user) 235 | { 236 | ikcpcb *kcp = (ikcpcb*)ikcp_malloc(sizeof(struct IKCPCB)); 237 | if (kcp == NULL) return NULL; 238 | kcp->conv = conv; 239 | kcp->token = token; 240 | kcp->user = user; 241 | kcp->snd_una = 0; 242 | kcp->snd_nxt = 0; 243 | kcp->rcv_nxt = 0; 244 | kcp->ts_recent = 0; 245 | kcp->ts_lastack = 0; 246 | kcp->ts_probe = 0; 247 | kcp->probe_wait = 0; 248 | kcp->snd_wnd = IKCP_WND_SND; 249 | kcp->rcv_wnd = IKCP_WND_RCV; 250 | kcp->rmt_wnd = IKCP_WND_RCV; 251 | kcp->cwnd = 0; 252 | kcp->incr = 0; 253 | kcp->probe = 0; 254 | kcp->mtu = IKCP_MTU_DEF; 255 | kcp->mss = kcp->mtu - IKCP_OVERHEAD; 256 | 257 | kcp->buffer = (char*)ikcp_malloc((kcp->mtu + IKCP_OVERHEAD) * 3); 258 | if (kcp->buffer == NULL) { 259 | ikcp_free(kcp); 260 | return NULL; 261 | } 262 | 263 | iqueue_init(&kcp->snd_queue); 264 | iqueue_init(&kcp->rcv_queue); 265 | iqueue_init(&kcp->snd_buf); 266 | iqueue_init(&kcp->rcv_buf); 267 | kcp->nrcv_buf = 0; 268 | kcp->nsnd_buf = 0; 269 | kcp->nrcv_que = 0; 270 | kcp->nsnd_que = 0; 271 | kcp->state = 0; 272 | kcp->acklist = NULL; 273 | kcp->ackblock = 0; 274 | kcp->ackcount = 0; 275 | kcp->rx_srtt = 0; 276 | kcp->rx_rttval = 0; 277 | kcp->rx_rto = IKCP_RTO_DEF; 278 | kcp->rx_minrto = IKCP_RTO_MIN; 279 | kcp->current = 0; 280 | kcp->interval = IKCP_INTERVAL; 281 | kcp->ts_flush = IKCP_INTERVAL; 282 | kcp->nodelay = 0; 283 | kcp->updated = 0; 284 | kcp->logmask = 0; 285 | kcp->ssthresh = IKCP_THRESH_INIT; 286 | kcp->fastresend = 0; 287 | kcp->nocwnd = 0; 288 | kcp->xmit = 0; 289 | kcp->dead_link = IKCP_DEADLINK; 290 | kcp->output = NULL; 291 | kcp->writelog = NULL; 292 | 293 | return kcp; 294 | } 295 | 296 | 297 | //--------------------------------------------------------------------- 298 | // release a new kcpcb 299 | //--------------------------------------------------------------------- 300 | void ikcp_release(ikcpcb *kcp) 301 | { 302 | assert(kcp); 303 | if (kcp) { 304 | IKCPSEG *seg; 305 | while (!iqueue_is_empty(&kcp->snd_buf)) { 306 | seg = iqueue_entry(kcp->snd_buf.next, IKCPSEG, node); 307 | iqueue_del(&seg->node); 308 | ikcp_segment_delete(kcp, seg); 309 | } 310 | while (!iqueue_is_empty(&kcp->rcv_buf)) { 311 | seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); 312 | iqueue_del(&seg->node); 313 | ikcp_segment_delete(kcp, seg); 314 | } 315 | while (!iqueue_is_empty(&kcp->snd_queue)) { 316 | seg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); 317 | iqueue_del(&seg->node); 318 | ikcp_segment_delete(kcp, seg); 319 | } 320 | while (!iqueue_is_empty(&kcp->rcv_queue)) { 321 | seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); 322 | iqueue_del(&seg->node); 323 | ikcp_segment_delete(kcp, seg); 324 | } 325 | if (kcp->buffer) { 326 | ikcp_free(kcp->buffer); 327 | } 328 | if (kcp->acklist) { 329 | ikcp_free(kcp->acklist); 330 | } 331 | 332 | kcp->nrcv_buf = 0; 333 | kcp->nsnd_buf = 0; 334 | kcp->nrcv_que = 0; 335 | kcp->nsnd_que = 0; 336 | kcp->ackcount = 0; 337 | kcp->buffer = NULL; 338 | kcp->acklist = NULL; 339 | ikcp_free(kcp); 340 | } 341 | } 342 | 343 | 344 | 345 | //--------------------------------------------------------------------- 346 | // user/upper level recv: returns size, returns below zero for EAGAIN 347 | //--------------------------------------------------------------------- 348 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len) 349 | { 350 | struct IQUEUEHEAD *p; 351 | int ispeek = (len < 0)? 1 : 0; 352 | int peeksize; 353 | int recover = 0; 354 | IKCPSEG *seg; 355 | assert(kcp); 356 | 357 | if (iqueue_is_empty(&kcp->rcv_queue)) 358 | return -1; 359 | 360 | if (len < 0) len = -len; 361 | 362 | peeksize = ikcp_peeksize(kcp); 363 | 364 | if (peeksize < 0) 365 | return -2; 366 | 367 | if (peeksize > len) 368 | return -3; 369 | 370 | if (kcp->nrcv_que >= kcp->rcv_wnd) 371 | recover = 1; 372 | 373 | // merge fragment 374 | for (len = 0, p = kcp->rcv_queue.next; p != &kcp->rcv_queue; ) { 375 | int fragment; 376 | seg = iqueue_entry(p, IKCPSEG, node); 377 | p = p->next; 378 | 379 | if (buffer) { 380 | memcpy(buffer, seg->data, seg->len); 381 | buffer += seg->len; 382 | } 383 | 384 | len += seg->len; 385 | fragment = seg->frg; 386 | 387 | if (ikcp_canlog(kcp, IKCP_LOG_RECV)) { 388 | ikcp_log(kcp, IKCP_LOG_RECV, "recv sn=%lu", seg->sn); 389 | } 390 | 391 | if (ispeek == 0) { 392 | iqueue_del(&seg->node); 393 | ikcp_segment_delete(kcp, seg); 394 | kcp->nrcv_que--; 395 | } 396 | 397 | if (fragment == 0) 398 | break; 399 | } 400 | 401 | assert(len == peeksize); 402 | 403 | // move available data from rcv_buf -> rcv_queue 404 | while (! iqueue_is_empty(&kcp->rcv_buf)) { 405 | IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); 406 | if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { 407 | iqueue_del(&seg->node); 408 | kcp->nrcv_buf--; 409 | iqueue_add_tail(&seg->node, &kcp->rcv_queue); 410 | kcp->nrcv_que++; 411 | kcp->rcv_nxt++; 412 | } else { 413 | break; 414 | } 415 | } 416 | 417 | // fast recover 418 | if (kcp->nrcv_que < kcp->rcv_wnd && recover) { 419 | // ready to send back IKCP_CMD_WINS in ikcp_flush 420 | // tell remote my window size 421 | kcp->probe |= IKCP_ASK_TELL; 422 | } 423 | 424 | return len; 425 | } 426 | 427 | 428 | //--------------------------------------------------------------------- 429 | // peek data size 430 | //--------------------------------------------------------------------- 431 | int ikcp_peeksize(const ikcpcb *kcp) 432 | { 433 | struct IQUEUEHEAD *p; 434 | IKCPSEG *seg; 435 | int length = 0; 436 | 437 | assert(kcp); 438 | 439 | if (iqueue_is_empty(&kcp->rcv_queue)) return -1; 440 | 441 | seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); 442 | if (seg->frg == 0) return seg->len; 443 | 444 | if (kcp->nrcv_que < seg->frg + 1) return -1; 445 | 446 | for (p = kcp->rcv_queue.next; p != &kcp->rcv_queue; p = p->next) { 447 | seg = iqueue_entry(p, IKCPSEG, node); 448 | length += seg->len; 449 | if (seg->frg == 0) break; 450 | } 451 | 452 | return length; 453 | } 454 | 455 | 456 | //--------------------------------------------------------------------- 457 | // user/upper level send, returns below zero for error 458 | //--------------------------------------------------------------------- 459 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len) 460 | { 461 | IKCPSEG *seg; 462 | int count, i; 463 | 464 | assert(kcp->mss > 0); 465 | if (len < 0) return -1; 466 | 467 | if (len <= (int)kcp->mss) count = 1; 468 | else count = (len + kcp->mss - 1) / kcp->mss; 469 | 470 | if (count > 255) return -2; 471 | 472 | if (count == 0) count = 1; 473 | 474 | // fragment 475 | for (i = 0; i < count; i++) { 476 | int size = len > (int)kcp->mss ? (int)kcp->mss : len; 477 | seg = ikcp_segment_new(kcp, size); 478 | assert(seg); 479 | if (seg == NULL) { 480 | return -2; 481 | } 482 | if (buffer && len > 0) { 483 | memcpy(seg->data, buffer, size); 484 | } 485 | seg->len = size; 486 | seg->frg = count - i - 1; 487 | iqueue_init(&seg->node); 488 | iqueue_add_tail(&seg->node, &kcp->snd_queue); 489 | kcp->nsnd_que++; 490 | if (buffer) { 491 | buffer += size; 492 | } 493 | len -= size; 494 | } 495 | 496 | return 0; 497 | } 498 | 499 | 500 | //--------------------------------------------------------------------- 501 | // parse ack 502 | //--------------------------------------------------------------------- 503 | static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt) 504 | { 505 | IINT32 rto = 0; 506 | if (kcp->rx_srtt == 0) { 507 | kcp->rx_srtt = rtt; 508 | kcp->rx_rttval = rtt / 2; 509 | } else { 510 | long delta = rtt - kcp->rx_srtt; 511 | if (delta < 0) delta = -delta; 512 | kcp->rx_rttval = (3 * kcp->rx_rttval + delta) / 4; 513 | kcp->rx_srtt = (7 * kcp->rx_srtt + rtt) / 8; 514 | if (kcp->rx_srtt < 1) kcp->rx_srtt = 1; 515 | } 516 | rto = kcp->rx_srtt + _imax_(1, 4 * kcp->rx_rttval); 517 | kcp->rx_rto = _ibound_(kcp->rx_minrto, rto, IKCP_RTO_MAX); 518 | } 519 | 520 | static void ikcp_shrink_buf(ikcpcb *kcp) 521 | { 522 | struct IQUEUEHEAD *p = kcp->snd_buf.next; 523 | if (p != &kcp->snd_buf) { 524 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 525 | kcp->snd_una = seg->sn; 526 | } else { 527 | kcp->snd_una = kcp->snd_nxt; 528 | } 529 | } 530 | 531 | static void ikcp_parse_ack(ikcpcb *kcp, IUINT32 sn) 532 | { 533 | struct IQUEUEHEAD *p, *next; 534 | 535 | if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) 536 | return; 537 | 538 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { 539 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 540 | next = p->next; 541 | if (sn == seg->sn) { 542 | iqueue_del(p); 543 | ikcp_segment_delete(kcp, seg); 544 | kcp->nsnd_buf--; 545 | break; 546 | } 547 | else { 548 | seg->fastack++; 549 | } 550 | } 551 | } 552 | 553 | static void ikcp_parse_una(ikcpcb *kcp, IUINT32 una) 554 | { 555 | #if 1 556 | struct IQUEUEHEAD *p, *next; 557 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { 558 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 559 | next = p->next; 560 | if (_itimediff(una, seg->sn) > 0) { 561 | iqueue_del(p); 562 | ikcp_segment_delete(kcp, seg); 563 | kcp->nsnd_buf--; 564 | } else { 565 | break; 566 | } 567 | } 568 | #endif 569 | } 570 | 571 | 572 | //--------------------------------------------------------------------- 573 | // ack append 574 | //--------------------------------------------------------------------- 575 | static void ikcp_ack_push(ikcpcb *kcp, IUINT32 sn, IUINT32 ts) 576 | { 577 | size_t newsize = kcp->ackcount + 1; 578 | IUINT32 *ptr; 579 | 580 | if (newsize > kcp->ackblock) { 581 | IUINT32 *acklist; 582 | size_t newblock; 583 | 584 | for (newblock = 8; newblock < newsize; newblock <<= 1); 585 | acklist = (IUINT32*)ikcp_malloc(newblock * sizeof(IUINT32) * 2); 586 | 587 | if (acklist == NULL) { 588 | assert(acklist != NULL); 589 | abort(); 590 | } 591 | 592 | if (kcp->acklist != NULL) { 593 | size_t x; 594 | for (x = 0; x < kcp->ackcount; x++) { 595 | acklist[x * 2 + 0] = kcp->acklist[x * 2 + 0]; 596 | acklist[x * 2 + 1] = kcp->acklist[x * 2 + 1]; 597 | } 598 | ikcp_free(kcp->acklist); 599 | } 600 | 601 | kcp->acklist = acklist; 602 | kcp->ackblock = newblock; 603 | } 604 | 605 | ptr = &kcp->acklist[kcp->ackcount * 2]; 606 | ptr[0] = sn; 607 | ptr[1] = ts; 608 | kcp->ackcount++; 609 | } 610 | 611 | static void ikcp_ack_get(const ikcpcb *kcp, int p, IUINT32 *sn, IUINT32 *ts) 612 | { 613 | if (sn) sn[0] = kcp->acklist[p * 2 + 0]; 614 | if (ts) ts[0] = kcp->acklist[p * 2 + 1]; 615 | } 616 | 617 | 618 | //--------------------------------------------------------------------- 619 | // parse data 620 | //--------------------------------------------------------------------- 621 | void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg) 622 | { 623 | struct IQUEUEHEAD *p, *prev; 624 | IUINT32 sn = newseg->sn; 625 | int repeat = 0; 626 | 627 | if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 || 628 | _itimediff(sn, kcp->rcv_nxt) < 0) { 629 | ikcp_segment_delete(kcp, newseg); 630 | return; 631 | } 632 | 633 | for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) { 634 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 635 | prev = p->prev; 636 | if (seg->sn == sn) { 637 | repeat = 1; 638 | break; 639 | } 640 | if (_itimediff(sn, seg->sn) > 0) { 641 | break; 642 | } 643 | } 644 | 645 | if (repeat == 0) { 646 | iqueue_init(&newseg->node); 647 | iqueue_add(&newseg->node, p); 648 | kcp->nrcv_buf++; 649 | } else { 650 | ikcp_segment_delete(kcp, newseg); 651 | } 652 | 653 | #if 0 654 | ikcp_qprint("rcvbuf", &kcp->rcv_buf); 655 | printf("rcv_nxt=%lu\n", kcp->rcv_nxt); 656 | #endif 657 | 658 | // move available data from rcv_buf -> rcv_queue 659 | while (! iqueue_is_empty(&kcp->rcv_buf)) { 660 | IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); 661 | if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { 662 | iqueue_del(&seg->node); 663 | kcp->nrcv_buf--; 664 | iqueue_add_tail(&seg->node, &kcp->rcv_queue); 665 | kcp->nrcv_que++; 666 | kcp->rcv_nxt++; 667 | } else { 668 | break; 669 | } 670 | } 671 | 672 | #if 0 673 | ikcp_qprint("queue", &kcp->rcv_queue); 674 | printf("rcv_nxt=%lu\n", kcp->rcv_nxt); 675 | #endif 676 | 677 | #if 1 678 | // printf("snd(buf=%d, queue=%d)\n", kcp->nsnd_buf, kcp->nsnd_que); 679 | // printf("rcv(buf=%d, queue=%d)\n", kcp->nrcv_buf, kcp->nrcv_que); 680 | #endif 681 | } 682 | 683 | 684 | //--------------------------------------------------------------------- 685 | // input data 686 | //--------------------------------------------------------------------- 687 | int ikcp_input(ikcpcb *kcp, const char *data, long size) 688 | { 689 | IUINT32 una = kcp->snd_una; 690 | 691 | if (ikcp_canlog(kcp, IKCP_LOG_INPUT)) { 692 | ikcp_log(kcp, IKCP_LOG_INPUT, "[RI] %d bytes", size); 693 | } 694 | 695 | if (data == NULL || size < 24) return 0; 696 | 697 | while (1) { 698 | IUINT32 ts, sn, len, una, conv, token; 699 | IUINT16 wnd; 700 | IUINT8 cmd, frg; 701 | IKCPSEG *seg; 702 | 703 | if (size < (int)IKCP_OVERHEAD) break; 704 | 705 | data = ikcp_decode32u(data, &conv); 706 | if (conv != kcp->conv) return -1; 707 | 708 | data = ikcp_decode32u(data, &token); 709 | if (token != kcp->token) return -1; 710 | 711 | data = ikcp_decode8u(data, &cmd); 712 | data = ikcp_decode8u(data, &frg); 713 | data = ikcp_decode16u(data, &wnd); 714 | data = ikcp_decode32u(data, &ts); 715 | data = ikcp_decode32u(data, &sn); 716 | data = ikcp_decode32u(data, &una); 717 | data = ikcp_decode32u(data, &len); 718 | 719 | size -= IKCP_OVERHEAD; 720 | 721 | if ((long)size < (long)len) return -2; 722 | 723 | if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK && 724 | cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS) 725 | return -3; 726 | 727 | kcp->rmt_wnd = wnd; 728 | ikcp_parse_una(kcp, una); 729 | ikcp_shrink_buf(kcp); 730 | 731 | if (cmd == IKCP_CMD_ACK) { 732 | if (_itimediff(kcp->current, ts) >= 0) { 733 | ikcp_update_ack(kcp, _itimediff(kcp->current, ts)); 734 | } 735 | ikcp_parse_ack(kcp, sn); 736 | ikcp_shrink_buf(kcp); 737 | if (ikcp_canlog(kcp, IKCP_LOG_IN_ACK)) { 738 | ikcp_log(kcp, IKCP_LOG_IN_DATA, 739 | "input ack: sn=%lu rtt=%ld rto=%ld", sn, 740 | (long)_itimediff(kcp->current, ts), 741 | (long)kcp->rx_rto); 742 | } 743 | } 744 | else if (cmd == IKCP_CMD_PUSH) { 745 | if (ikcp_canlog(kcp, IKCP_LOG_IN_DATA)) { 746 | ikcp_log(kcp, IKCP_LOG_IN_DATA, 747 | "input psh: sn=%lu ts=%lu", sn, ts); 748 | } 749 | if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) < 0) { 750 | ikcp_ack_push(kcp, sn, ts); 751 | if (_itimediff(sn, kcp->rcv_nxt) >= 0) { 752 | seg = ikcp_segment_new(kcp, len); 753 | seg->conv = conv; 754 | seg->token = token; 755 | seg->cmd = cmd; 756 | seg->frg = frg; 757 | seg->wnd = wnd; 758 | seg->ts = ts; 759 | seg->sn = sn; 760 | seg->una = una; 761 | seg->len = len; 762 | 763 | if (len > 0) { 764 | memcpy(seg->data, data, len); 765 | } 766 | 767 | ikcp_parse_data(kcp, seg); 768 | } 769 | } 770 | } 771 | else if (cmd == IKCP_CMD_WASK) { 772 | // ready to send back IKCP_CMD_WINS in ikcp_flush 773 | // tell remote my window size 774 | kcp->probe |= IKCP_ASK_TELL; 775 | if (ikcp_canlog(kcp, IKCP_LOG_IN_PROBE)) { 776 | ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe"); 777 | } 778 | } 779 | else if (cmd == IKCP_CMD_WINS) { 780 | // do nothing 781 | if (ikcp_canlog(kcp, IKCP_LOG_IN_WINS)) { 782 | ikcp_log(kcp, IKCP_LOG_IN_WINS, 783 | "input wins: %lu", (IUINT32)(wnd)); 784 | } 785 | } 786 | else { 787 | return -3; 788 | } 789 | 790 | data += len; 791 | size -= len; 792 | } 793 | 794 | if (_itimediff(kcp->snd_una, una) > 0) { 795 | if (kcp->cwnd < kcp->rmt_wnd) { 796 | IUINT32 mss = kcp->mss; 797 | if (kcp->cwnd < kcp->ssthresh) { 798 | kcp->cwnd++; 799 | kcp->incr += mss; 800 | } else { 801 | if (kcp->incr < mss) kcp->incr = mss; 802 | kcp->incr += (mss * mss) / kcp->incr + (mss / 16); 803 | if ((kcp->cwnd + 1) * mss >= kcp->incr) { 804 | kcp->cwnd++; 805 | } 806 | } 807 | if (kcp->cwnd > kcp->rmt_wnd) { 808 | kcp->cwnd = kcp->rmt_wnd; 809 | kcp->incr = kcp->rmt_wnd * mss; 810 | } 811 | } 812 | } 813 | 814 | return 0; 815 | } 816 | 817 | 818 | //--------------------------------------------------------------------- 819 | // ikcp_encode_seg 820 | //--------------------------------------------------------------------- 821 | static char *ikcp_encode_seg(char *ptr, const IKCPSEG *seg) 822 | { 823 | ptr = ikcp_encode32u(ptr, seg->conv); 824 | ptr = ikcp_encode32u(ptr, seg->token); 825 | ptr = ikcp_encode8u(ptr, (IUINT8)seg->cmd); 826 | ptr = ikcp_encode8u(ptr, (IUINT8)seg->frg); 827 | ptr = ikcp_encode16u(ptr, (IUINT16)seg->wnd); 828 | ptr = ikcp_encode32u(ptr, seg->ts); 829 | ptr = ikcp_encode32u(ptr, seg->sn); 830 | ptr = ikcp_encode32u(ptr, seg->una); 831 | ptr = ikcp_encode32u(ptr, seg->len); 832 | return ptr; 833 | } 834 | 835 | static int ikcp_wnd_unused(const ikcpcb *kcp) 836 | { 837 | if (kcp->nrcv_que < kcp->rcv_wnd) { 838 | return kcp->rcv_wnd - kcp->nrcv_que; 839 | } 840 | return 0; 841 | } 842 | 843 | 844 | //--------------------------------------------------------------------- 845 | // ikcp_flush 846 | //--------------------------------------------------------------------- 847 | void ikcp_flush(ikcpcb *kcp) 848 | { 849 | IUINT32 current = kcp->current; 850 | char *buffer = kcp->buffer; 851 | char *ptr = buffer; 852 | int count, size, i; 853 | IUINT32 resent, cwnd; 854 | IUINT32 rtomin; 855 | struct IQUEUEHEAD *p; 856 | int change = 0; 857 | int lost = 0; 858 | IKCPSEG seg; 859 | 860 | // 'ikcp_update' haven't been called. 861 | if (kcp->updated == 0) return; 862 | 863 | seg.conv = kcp->conv; 864 | seg.token = kcp->token; 865 | seg.cmd = IKCP_CMD_ACK; 866 | seg.frg = 0; 867 | seg.wnd = ikcp_wnd_unused(kcp); 868 | seg.una = kcp->rcv_nxt; 869 | seg.len = 0; 870 | seg.sn = 0; 871 | seg.ts = 0; 872 | 873 | // flush acknowledges 874 | count = kcp->ackcount; 875 | for (i = 0; i < count; i++) { 876 | size = (int)(ptr - buffer); 877 | if (size + IKCP_OVERHEAD > (int)kcp->mtu) { 878 | ikcp_output(kcp, buffer, size); 879 | ptr = buffer; 880 | } 881 | ikcp_ack_get(kcp, i, &seg.sn, &seg.ts); 882 | ptr = ikcp_encode_seg(ptr, &seg); 883 | } 884 | 885 | kcp->ackcount = 0; 886 | 887 | // probe window size (if remote window size equals zero) 888 | if (kcp->rmt_wnd == 0) { 889 | if (kcp->probe_wait == 0) { 890 | kcp->probe_wait = IKCP_PROBE_INIT; 891 | kcp->ts_probe = kcp->current + kcp->probe_wait; 892 | } 893 | else { 894 | if (_itimediff(kcp->current, kcp->ts_probe) >= 0) { 895 | if (kcp->probe_wait < IKCP_PROBE_INIT) 896 | kcp->probe_wait = IKCP_PROBE_INIT; 897 | kcp->probe_wait += kcp->probe_wait / 2; 898 | if (kcp->probe_wait > IKCP_PROBE_LIMIT) 899 | kcp->probe_wait = IKCP_PROBE_LIMIT; 900 | kcp->ts_probe = kcp->current + kcp->probe_wait; 901 | kcp->probe |= IKCP_ASK_SEND; 902 | } 903 | } 904 | } else { 905 | kcp->ts_probe = 0; 906 | kcp->probe_wait = 0; 907 | } 908 | 909 | // flush window probing commands 910 | if (kcp->probe & IKCP_ASK_SEND) { 911 | seg.cmd = IKCP_CMD_WASK; 912 | size = (int)(ptr - buffer); 913 | if (size + IKCP_OVERHEAD > (int)kcp->mtu) { 914 | ikcp_output(kcp, buffer, size); 915 | ptr = buffer; 916 | } 917 | ptr = ikcp_encode_seg(ptr, &seg); 918 | } 919 | 920 | // flush window probing commands 921 | if (kcp->probe & IKCP_ASK_TELL) { 922 | seg.cmd = IKCP_CMD_WINS; 923 | size = (int)(ptr - buffer); 924 | if (size + IKCP_OVERHEAD > (int)kcp->mtu) { 925 | ikcp_output(kcp, buffer, size); 926 | ptr = buffer; 927 | } 928 | ptr = ikcp_encode_seg(ptr, &seg); 929 | } 930 | 931 | kcp->probe = 0; 932 | 933 | // calculate window size 934 | cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd); 935 | if (kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd); 936 | 937 | // move data from snd_queue to snd_buf 938 | while (_itimediff(kcp->snd_nxt, kcp->snd_una + cwnd) < 0) { 939 | IKCPSEG *newseg; 940 | if (iqueue_is_empty(&kcp->snd_queue)) break; 941 | 942 | newseg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); 943 | 944 | iqueue_del(&newseg->node); 945 | iqueue_add_tail(&newseg->node, &kcp->snd_buf); 946 | kcp->nsnd_que--; 947 | kcp->nsnd_buf++; 948 | 949 | newseg->conv = kcp->conv; 950 | newseg->token = kcp->token; 951 | newseg->cmd = IKCP_CMD_PUSH; 952 | newseg->wnd = seg.wnd; 953 | newseg->ts = current; 954 | newseg->sn = kcp->snd_nxt++; 955 | newseg->una = kcp->rcv_nxt; 956 | newseg->resendts = current; 957 | newseg->rto = kcp->rx_rto; 958 | newseg->fastack = 0; 959 | newseg->xmit = 0; 960 | } 961 | 962 | // calculate resent 963 | resent = (kcp->fastresend > 0)? (IUINT32)kcp->fastresend : 0xffffffff; 964 | rtomin = (kcp->nodelay == 0)? (kcp->rx_rto >> 3) : 0; 965 | 966 | // flush data segments 967 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { 968 | IKCPSEG *segment = iqueue_entry(p, IKCPSEG, node); 969 | int needsend = 0; 970 | if (segment->xmit == 0) { 971 | needsend = 1; 972 | segment->xmit++; 973 | segment->rto = kcp->rx_rto; 974 | segment->resendts = current + segment->rto + rtomin; 975 | } 976 | else if (_itimediff(current, segment->resendts) >= 0) { 977 | needsend = 1; 978 | segment->xmit++; 979 | kcp->xmit++; 980 | if (kcp->nodelay == 0) { 981 | segment->rto += kcp->rx_rto; 982 | } else { 983 | segment->rto += kcp->rx_rto / 2; 984 | } 985 | segment->resendts = current + segment->rto; 986 | lost = 1; 987 | } 988 | else if (segment->fastack >= resent) { 989 | needsend = 1; 990 | segment->xmit++; 991 | segment->fastack = 0; 992 | segment->resendts = current + segment->rto; 993 | change++; 994 | } 995 | 996 | if (needsend) { 997 | int size, need; 998 | segment->ts = current; 999 | segment->wnd = seg.wnd; 1000 | segment->una = kcp->rcv_nxt; 1001 | 1002 | size = (int)(ptr - buffer); 1003 | need = IKCP_OVERHEAD + segment->len; 1004 | 1005 | if (size + need > (int)kcp->mtu) { 1006 | ikcp_output(kcp, buffer, size); 1007 | ptr = buffer; 1008 | } 1009 | 1010 | ptr = ikcp_encode_seg(ptr, segment); 1011 | 1012 | if (segment->len > 0) { 1013 | memcpy(ptr, segment->data, segment->len); 1014 | ptr += segment->len; 1015 | } 1016 | 1017 | if (segment->xmit >= kcp->dead_link) { 1018 | kcp->state = -1; 1019 | } 1020 | } 1021 | } 1022 | 1023 | // flash remain segments 1024 | size = (int)(ptr - buffer); 1025 | if (size > 0) { 1026 | ikcp_output(kcp, buffer, size); 1027 | } 1028 | 1029 | // update ssthresh 1030 | if (change) { 1031 | IUINT32 inflight = kcp->snd_nxt - kcp->snd_una; 1032 | kcp->ssthresh = inflight / 2; 1033 | if (kcp->ssthresh < IKCP_THRESH_MIN) 1034 | kcp->ssthresh = IKCP_THRESH_MIN; 1035 | kcp->cwnd = kcp->ssthresh + resent; 1036 | kcp->incr = kcp->cwnd * kcp->mss; 1037 | } 1038 | 1039 | if (lost) { 1040 | kcp->ssthresh = cwnd / 2; 1041 | if (kcp->ssthresh < IKCP_THRESH_MIN) 1042 | kcp->ssthresh = IKCP_THRESH_MIN; 1043 | kcp->cwnd = 1; 1044 | kcp->incr = kcp->mss; 1045 | } 1046 | 1047 | if (kcp->cwnd < 1) { 1048 | kcp->cwnd = 1; 1049 | kcp->incr = kcp->mss; 1050 | } 1051 | } 1052 | 1053 | 1054 | //--------------------------------------------------------------------- 1055 | // update state (call it repeatedly, every 10ms-100ms), or you can ask 1056 | // ikcp_check when to call it again (without ikcp_input/_send calling). 1057 | // 'current' - current timestamp in millisec. 1058 | //--------------------------------------------------------------------- 1059 | void ikcp_update(ikcpcb *kcp, IUINT32 current) 1060 | { 1061 | IINT32 slap; 1062 | 1063 | kcp->current = current; 1064 | 1065 | if (kcp->updated == 0) { 1066 | kcp->updated = 1; 1067 | kcp->ts_flush = kcp->current; 1068 | } 1069 | 1070 | slap = _itimediff(kcp->current, kcp->ts_flush); 1071 | 1072 | if (slap >= 10000 || slap < -10000) { 1073 | kcp->ts_flush = kcp->current; 1074 | slap = 0; 1075 | } 1076 | 1077 | if (slap >= 0) { 1078 | kcp->ts_flush += kcp->interval; 1079 | if (_itimediff(kcp->current, kcp->ts_flush) >= 0) { 1080 | kcp->ts_flush = kcp->current + kcp->interval; 1081 | } 1082 | ikcp_flush(kcp); 1083 | } 1084 | } 1085 | 1086 | 1087 | //--------------------------------------------------------------------- 1088 | // Determine when should you invoke ikcp_update: 1089 | // returns when you should invoke ikcp_update in millisec, if there 1090 | // is no ikcp_input/_send calling. you can call ikcp_update in that 1091 | // time, instead of call update repeatly. 1092 | // Important to reduce unnacessary ikcp_update invoking. use it to 1093 | // schedule ikcp_update (eg. implementing an epoll-like mechanism, 1094 | // or optimize ikcp_update when handling massive kcp connections) 1095 | //--------------------------------------------------------------------- 1096 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current) 1097 | { 1098 | IUINT32 ts_flush = kcp->ts_flush; 1099 | IINT32 tm_flush = 0x7fffffff; 1100 | IINT32 tm_packet = 0x7fffffff; 1101 | IUINT32 minimal = 0; 1102 | struct IQUEUEHEAD *p; 1103 | 1104 | if (kcp->updated == 0) { 1105 | return current; 1106 | } 1107 | 1108 | if (_itimediff(current, ts_flush) >= 10000 || 1109 | _itimediff(current, ts_flush) < -10000) { 1110 | ts_flush = current; 1111 | } 1112 | 1113 | if (_itimediff(current, ts_flush) >= 0) { 1114 | return current; 1115 | } 1116 | 1117 | tm_flush = _itimediff(ts_flush, current); 1118 | 1119 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { 1120 | const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); 1121 | IINT32 diff = _itimediff(seg->resendts, current); 1122 | if (diff <= 0) { 1123 | return current; 1124 | } 1125 | if (diff < tm_packet) tm_packet = diff; 1126 | } 1127 | 1128 | minimal = (IUINT32)(tm_packet < tm_flush ? tm_packet : tm_flush); 1129 | if (minimal >= kcp->interval) minimal = kcp->interval; 1130 | 1131 | return current + minimal; 1132 | } 1133 | 1134 | 1135 | 1136 | int ikcp_setmtu(ikcpcb *kcp, int mtu) 1137 | { 1138 | char *buffer; 1139 | if (mtu < 50 || mtu < (int)IKCP_OVERHEAD) 1140 | return -1; 1141 | buffer = (char*)ikcp_malloc((mtu + IKCP_OVERHEAD) * 3); 1142 | if (buffer == NULL) 1143 | return -2; 1144 | kcp->mtu = mtu; 1145 | kcp->mss = kcp->mtu - IKCP_OVERHEAD; 1146 | ikcp_free(kcp->buffer); 1147 | kcp->buffer = buffer; 1148 | return 0; 1149 | } 1150 | 1151 | int ikcp_interval(ikcpcb *kcp, int interval) 1152 | { 1153 | if (interval > 5000) interval = 5000; 1154 | else if (interval < 10) interval = 10; 1155 | kcp->interval = interval; 1156 | return 0; 1157 | } 1158 | 1159 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc) 1160 | { 1161 | if (nodelay >= 0) { 1162 | kcp->nodelay = nodelay; 1163 | if (nodelay) { 1164 | kcp->rx_minrto = IKCP_RTO_NDL; 1165 | } 1166 | else { 1167 | kcp->rx_minrto = IKCP_RTO_MIN; 1168 | } 1169 | } 1170 | if (interval >= 0) { 1171 | if (interval > 5000) interval = 5000; 1172 | else if (interval < 10) interval = 10; 1173 | kcp->interval = interval; 1174 | } 1175 | if (resend >= 0) { 1176 | kcp->fastresend = resend; 1177 | } 1178 | if (nc >= 0) { 1179 | kcp->nocwnd = nc; 1180 | } 1181 | return 0; 1182 | } 1183 | 1184 | 1185 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd) 1186 | { 1187 | if (kcp) { 1188 | if (sndwnd > 0) { 1189 | kcp->snd_wnd = sndwnd; 1190 | } 1191 | if (rcvwnd > 0) { 1192 | kcp->rcv_wnd = rcvwnd; 1193 | } 1194 | } 1195 | return 0; 1196 | } 1197 | 1198 | int ikcp_waitsnd(const ikcpcb *kcp) 1199 | { 1200 | return kcp->nsnd_buf + kcp->nsnd_que; 1201 | } 1202 | 1203 | 1204 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------