mt-lang 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/index.html +4 -3
- data/lib/milk_tea/base.rb +1 -1
- data/std/asset_pack.mt +267 -0
- data/std/async/libuv_runtime.mt +539 -0
- data/std/async/mailbox.mt +191 -0
- data/std/async/runtime.mt +87 -0
- data/std/async.mt +87 -0
- data/std/base64.mt +185 -0
- data/std/behavior_tree.mt +396 -0
- data/std/binary.mt +314 -0
- data/std/binary_heap.mt +157 -0
- data/std/bitset.mt +239 -0
- data/std/box2d.mt +535 -0
- data/std/bytes.mt +46 -0
- data/std/c/box2d.mt +1254 -0
- data/std/c/cgltf.mt +647 -0
- data/std/c/cjson.mt +106 -0
- data/std/c/crypto.mt +11 -0
- data/std/c/crypto_support.h +51 -0
- data/std/c/ctype.mt +19 -0
- data/std/c/ctype_bindgen.h +29 -0
- data/std/c/curl.mt +1223 -0
- data/std/c/enet.mt +473 -0
- data/std/c/errno.mt +41 -0
- data/std/c/errno_bindgen.h +53 -0
- data/std/c/flecs.mt +2945 -0
- data/std/c/fs.linux.mt +43 -0
- data/std/c/fs.windows.mt +43 -0
- data/std/c/fs_support.h +954 -0
- data/std/c/gl.mt +2065 -0
- data/std/c/gl_registry_helpers.h +10748 -0
- data/std/c/glfw.mt +499 -0
- data/std/c/libc.mt +167 -0
- data/std/c/libuv.mt +1491 -0
- data/std/c/math.mt +22 -0
- data/std/c/math_bindgen.h +31 -0
- data/std/c/miniaudio.mt +3420 -0
- data/std/c/pcre2.mt +735 -0
- data/std/c/process.mt +58 -0
- data/std/c/process_support.h +1147 -0
- data/std/c/raygui.mt +1602 -0
- data/std/c/raylib.mt +1245 -0
- data/std/c/raymath.mt +163 -0
- data/std/c/rlgl.mt +434 -0
- data/std/c/rpng.mt +60 -0
- data/std/c/rres.mt +187 -0
- data/std/c/sdl3.mt +4132 -0
- data/std/c/sqlite3.mt +951 -0
- data/std/c/stb_image.mt +52 -0
- data/std/c/stb_image_resize2.mt +128 -0
- data/std/c/stb_image_write.mt +19 -0
- data/std/c/stb_rect_pack.mt +38 -0
- data/std/c/stb_truetype.mt +175 -0
- data/std/c/stb_vorbis.mt +31 -0
- data/std/c/stdio.mt +90 -0
- data/std/c/steamworks.h +5769 -0
- data/std/c/steamworks.mt +4771 -0
- data/std/c/string.mt +16 -0
- data/std/c/string_bindgen.h +36 -0
- data/std/c/sync.mt +33 -0
- data/std/c/sync_support.h +219 -0
- data/std/c/terminal.mt +24 -0
- data/std/c/terminal_support.h +408 -0
- data/std/c/time.mt +26 -0
- data/std/c/tls.mt +24 -0
- data/std/c/tls_support.h +626 -0
- data/std/c/tracy.mt +53 -0
- data/std/c/zlib.mt +20 -0
- data/std/c/zlib_support.h +239 -0
- data/std/c/zstd.mt +231 -0
- data/std/cell.mt +60 -0
- data/std/cgltf.mt +115 -0
- data/std/cjson.mt +90 -0
- data/std/cli.mt +768 -0
- data/std/cookie.mt +198 -0
- data/std/counter.mt +160 -0
- data/std/crypto.mt +74 -0
- data/std/cstring.mt +13 -0
- data/std/ctype.mt +52 -0
- data/std/curl/runtime.mt +261 -0
- data/std/curl.mt +38 -0
- data/std/deque.mt +348 -0
- data/std/encoding.mt +93 -0
- data/std/enet.mt +133 -0
- data/std/env.mt +41 -0
- data/std/errno.mt +47 -0
- data/std/flecs.mt +978 -0
- data/std/fmt.mt +271 -0
- data/std/fs.linux.mt +696 -0
- data/std/fs.windows.mt +711 -0
- data/std/fsm.mt +267 -0
- data/std/gl.mt +2062 -0
- data/std/glfw.mt +471 -0
- data/std/goap.mt +342 -0
- data/std/graph.mt +521 -0
- data/std/gzip.mt +77 -0
- data/std/hash.mt +429 -0
- data/std/http/server.mt +810 -0
- data/std/http.mt +1227 -0
- data/std/intern.mt +53 -0
- data/std/jobs.mt +288 -0
- data/std/json.mt +627 -0
- data/std/libc.mt +34 -0
- data/std/libuv.mt +554 -0
- data/std/linear_algebra.mt +243 -0
- data/std/linked_map.mt +372 -0
- data/std/linked_map_view.mt +49 -0
- data/std/linked_set.mt +117 -0
- data/std/log.mt +69 -0
- data/std/map.mt +418 -0
- data/std/math.mt +24 -0
- data/std/mem/arena.mt +137 -0
- data/std/mem/endian.mt +51 -0
- data/std/mem/heap.mt +298 -0
- data/std/mem/pool.mt +164 -0
- data/std/mem/stack.mt +47 -0
- data/std/mem/tracking.mt +119 -0
- data/std/miniaudio.mt +1299 -0
- data/std/multiset.mt +191 -0
- data/std/net/channel.mt +727 -0
- data/std/net/clock.mt +237 -0
- data/std/net/discovery.mt +277 -0
- data/std/net/lobby.mt +813 -0
- data/std/net/manager.mt +539 -0
- data/std/net/mux.mt +839 -0
- data/std/net/nat.mt +122 -0
- data/std/net/packet.mt +219 -0
- data/std/net/punch.mt +162 -0
- data/std/net/rpc.mt +119 -0
- data/std/net/session.mt +1157 -0
- data/std/net/stun.mt +252 -0
- data/std/net/sync.mt +183 -0
- data/std/net/turn.mt +459 -0
- data/std/net.mt +2944 -0
- data/std/oauth2.mt +408 -0
- data/std/option.mt +51 -0
- data/std/ordered_map.mt +497 -0
- data/std/ordered_set.mt +348 -0
- data/std/path.mt +368 -0
- data/std/pcre2/runtime.mt +35 -0
- data/std/pcre2.mt +78 -0
- data/std/priority_queue.mt +55 -0
- data/std/process.mt +837 -0
- data/std/queue.mt +55 -0
- data/std/random.mt +146 -0
- data/std/raygui.mt +99 -0
- data/std/raylib/debug_console.mt +71 -0
- data/std/raylib/easing.mt +388 -0
- data/std/raylib/packed_assets.mt +255 -0
- data/std/raylib/runtime.mt +30 -0
- data/std/raylib/tracy_gpu.mt +37 -0
- data/std/raylib.mt +1510 -0
- data/std/raymath.mt +153 -0
- data/std/result.mt +89 -0
- data/std/rlgl.mt +268 -0
- data/std/rpng.mt +46 -0
- data/std/rres.mt +36 -0
- data/std/sdl3/runtime.mt +54 -0
- data/std/sdl3.mt +1731 -0
- data/std/serialize.mt +68 -0
- data/std/set.mt +124 -0
- data/std/spatial.mt +176 -0
- data/std/sqlite3.mt +151 -0
- data/std/stack.mt +55 -0
- data/std/stb_image.mt +47 -0
- data/std/stb_image_resize2.mt +41 -0
- data/std/stb_image_write.mt +17 -0
- data/std/stb_rect_pack.mt +15 -0
- data/std/stb_truetype.mt +77 -0
- data/std/stb_vorbis.mt +16 -0
- data/std/stdio.mt +88 -0
- data/std/steamworks.mt +1542 -0
- data/std/str.mt +293 -0
- data/std/string.mt +234 -0
- data/std/sync.mt +194 -0
- data/std/tar.mt +704 -0
- data/std/terminal.mt +1002 -0
- data/std/testing.mt +266 -0
- data/std/thread.mt +120 -0
- data/std/time.mt +105 -0
- data/std/tls.mt +616 -0
- data/std/toml.mt +1310 -0
- data/std/tracy.mt +42 -0
- data/std/uri.mt +118 -0
- data/std/url.mt +372 -0
- data/std/vec.mt +433 -0
- data/std/zstd.mt +94 -0
- metadata +187 -2
data/std/net/session.mt
ADDED
|
@@ -0,0 +1,1157 @@
|
|
|
1
|
+
import std.async as aio
|
|
2
|
+
import std.binary as bin
|
|
3
|
+
import std.bytes as bytes
|
|
4
|
+
import std.crypto as crypto
|
|
5
|
+
import std.deque as deque
|
|
6
|
+
import std.net as net
|
|
7
|
+
import std.net.channel as chan
|
|
8
|
+
import std.string as string
|
|
9
|
+
import std.vec as vec
|
|
10
|
+
|
|
11
|
+
const msg_type_user: ubyte = 0x00
|
|
12
|
+
const msg_type_connect_request: ubyte = 0x01
|
|
13
|
+
const msg_type_connect_accept: ubyte = 0x02
|
|
14
|
+
const msg_type_connect_reject: ubyte = 0x03
|
|
15
|
+
const msg_type_heartbeat: ubyte = 0x04
|
|
16
|
+
const msg_type_heartbeat_ack: ubyte = 0x05
|
|
17
|
+
const msg_type_disconnect: ubyte = 0x06
|
|
18
|
+
|
|
19
|
+
const reject_reason_version: ubyte = 1
|
|
20
|
+
const reject_reason_full: ubyte = 2
|
|
21
|
+
const reject_reason_refused: ubyte = 3
|
|
22
|
+
const disconnect_reason_local: ubyte = 1
|
|
23
|
+
const disconnect_reason_timeout: ubyte = 2
|
|
24
|
+
const disconnect_reason_remote: ubyte = 3
|
|
25
|
+
|
|
26
|
+
const connect_request_payload_bytes: ptr_uint = 12
|
|
27
|
+
const connect_accept_payload_bytes: ptr_uint = 4
|
|
28
|
+
const heartbeat_payload_bytes: ptr_uint = 4
|
|
29
|
+
|
|
30
|
+
type ChanMessageTask = Task[Result[Option[chan.Message], net.Error]]
|
|
31
|
+
type ChanHostMessageTask = Task[Result[Option[chan.HostMessage], net.Error]]
|
|
32
|
+
|
|
33
|
+
public enum ConnectionState: int
|
|
34
|
+
disconnected = 0
|
|
35
|
+
connecting = 1
|
|
36
|
+
connected = 2
|
|
37
|
+
|
|
38
|
+
public struct Config:
|
|
39
|
+
max_payload_bytes: ptr_uint
|
|
40
|
+
protocol_version: uint
|
|
41
|
+
heartbeat_interval: uint
|
|
42
|
+
heartbeat_timeout: uint
|
|
43
|
+
resend_after_frames: uint
|
|
44
|
+
max_pending_reliable: ptr_uint
|
|
45
|
+
|
|
46
|
+
public enum PeerEventKind: ubyte
|
|
47
|
+
user_data = 0
|
|
48
|
+
peer_joined = 1
|
|
49
|
+
peer_left = 2
|
|
50
|
+
|
|
51
|
+
public struct PeerEvent:
|
|
52
|
+
kind: PeerEventKind
|
|
53
|
+
peer_id: uint
|
|
54
|
+
payload: bytes.Bytes
|
|
55
|
+
reliable: bool
|
|
56
|
+
reason: ubyte
|
|
57
|
+
|
|
58
|
+
public struct Connection:
|
|
59
|
+
channel: chan.Channel
|
|
60
|
+
state: ConnectionState
|
|
61
|
+
protocol_version: uint
|
|
62
|
+
heartbeat_interval: uint
|
|
63
|
+
heartbeat_timeout: uint
|
|
64
|
+
frame_since_last_recv: uint
|
|
65
|
+
ping_rtt_frames: uint
|
|
66
|
+
last_heartbeat_sent: uint
|
|
67
|
+
pending_recv: Option[ChanMessageTask]
|
|
68
|
+
event_queue: deque.Deque[PeerEvent]
|
|
69
|
+
outgoing: deque.Deque[OutgoingMessage]
|
|
70
|
+
|
|
71
|
+
public struct PeerConnection:
|
|
72
|
+
peer_id: uint
|
|
73
|
+
channel_state: ConnectionState
|
|
74
|
+
frame_since_last_recv: uint
|
|
75
|
+
last_heartbeat_sent: uint
|
|
76
|
+
heartbeat_ack_received: bool
|
|
77
|
+
|
|
78
|
+
public struct Session:
|
|
79
|
+
host: chan.Host
|
|
80
|
+
config: Config
|
|
81
|
+
next_peer_id: uint
|
|
82
|
+
peers: vec.Vec[PeerConnection]
|
|
83
|
+
pending_recv: Option[ChanHostMessageTask]
|
|
84
|
+
event_queue: deque.Deque[PeerEvent]
|
|
85
|
+
outgoing: deque.Deque[OutgoingMessage]
|
|
86
|
+
|
|
87
|
+
struct OutgoingMessage:
|
|
88
|
+
address: net.SocketAddress
|
|
89
|
+
payload: bytes.Bytes
|
|
90
|
+
reliable: bool
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
extending PeerEvent:
|
|
94
|
+
public editable function release() -> void:
|
|
95
|
+
if this.kind == PeerEventKind.user_data:
|
|
96
|
+
this.payload.release()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
function build_connect_request(version: uint, key: ulong) -> bytes.Bytes:
|
|
100
|
+
var w = bin.Writer.with_capacity(1 + connect_request_payload_bytes)
|
|
101
|
+
w.write_ubyte(msg_type_connect_request)
|
|
102
|
+
w.write_uint(version)
|
|
103
|
+
w.write_ulong(key)
|
|
104
|
+
return w.finish()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
function build_connect_accept(peer_id: uint) -> bytes.Bytes:
|
|
108
|
+
var w = bin.Writer.with_capacity(1 + connect_accept_payload_bytes)
|
|
109
|
+
w.write_ubyte(msg_type_connect_accept)
|
|
110
|
+
w.write_uint(peer_id)
|
|
111
|
+
return w.finish()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
function build_connect_reject(reason: ubyte) -> bytes.Bytes:
|
|
115
|
+
var w = bin.Writer.with_capacity(2)
|
|
116
|
+
w.write_ubyte(msg_type_connect_reject)
|
|
117
|
+
w.write_ubyte(reason)
|
|
118
|
+
return w.finish()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
function build_heartbeat(frame: uint) -> bytes.Bytes:
|
|
122
|
+
var w = bin.Writer.with_capacity(1 + heartbeat_payload_bytes)
|
|
123
|
+
w.write_ubyte(msg_type_heartbeat)
|
|
124
|
+
w.write_uint(frame)
|
|
125
|
+
return w.finish()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
function build_heartbeat_ack(frame: uint) -> bytes.Bytes:
|
|
129
|
+
var w = bin.Writer.with_capacity(1 + heartbeat_payload_bytes)
|
|
130
|
+
w.write_ubyte(msg_type_heartbeat_ack)
|
|
131
|
+
w.write_uint(frame)
|
|
132
|
+
return w.finish()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
function build_disconnect(reason: ubyte) -> bytes.Bytes:
|
|
136
|
+
var w = bin.Writer.with_capacity(2)
|
|
137
|
+
w.write_ubyte(msg_type_disconnect)
|
|
138
|
+
w.write_ubyte(reason)
|
|
139
|
+
return w.finish()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
function build_user_data(payload: span[ubyte]) -> bytes.Bytes:
|
|
143
|
+
var w = bin.Writer.with_capacity(1 + payload.len)
|
|
144
|
+
w.write_ubyte(msg_type_user)
|
|
145
|
+
w.write_bytes(payload)
|
|
146
|
+
return w.finish()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
function generate_handshake_key() -> Result[ulong, net.Error]:
|
|
150
|
+
let key_result = crypto.random_bytes(8)
|
|
151
|
+
match key_result:
|
|
152
|
+
Result.failure as p:
|
|
153
|
+
return Result[ulong, net.Error].failure(error = net.Error(code = -1, message = p.error.message))
|
|
154
|
+
Result.success as payload:
|
|
155
|
+
var rand = payload.value
|
|
156
|
+
var reader = bin.reader(rand.as_span())
|
|
157
|
+
match reader.read_ulong():
|
|
158
|
+
Result.failure as rp:
|
|
159
|
+
rand.release()
|
|
160
|
+
return Result[ulong, net.Error].failure(error = net.Error(
|
|
161
|
+
code = rp.error.code,
|
|
162
|
+
message = rp.error.message
|
|
163
|
+
))
|
|
164
|
+
Result.success as read_payload:
|
|
165
|
+
let value = read_payload.value
|
|
166
|
+
rand.release()
|
|
167
|
+
return Result[ulong, net.Error].success(value = value)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
function decode_u32_at(data: span[ubyte], offset: ptr_uint) -> uint:
|
|
171
|
+
unsafe:
|
|
172
|
+
return uint<-read(data.data + offset) | (uint<-read(data.data + offset + 1) << 8) | (uint<-read(data.data + offset + 2) << 16) | (uint<-read(data.data + offset + 3) << 24)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
function channel_config_for(config: Config) -> chan.Config:
|
|
176
|
+
return chan.Config(
|
|
177
|
+
max_payload_bytes = config.max_payload_bytes + 1,
|
|
178
|
+
max_pending_reliable = config.max_pending_reliable,
|
|
179
|
+
resend_after_frames = config.resend_after_frames
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
extending Config:
|
|
184
|
+
public static function default(max_payload_bytes: ptr_uint) -> Config:
|
|
185
|
+
return Config(
|
|
186
|
+
max_payload_bytes = max_payload_bytes,
|
|
187
|
+
protocol_version = 1,
|
|
188
|
+
heartbeat_interval = 60,
|
|
189
|
+
heartbeat_timeout = 300,
|
|
190
|
+
resend_after_frames = 5,
|
|
191
|
+
max_pending_reliable = 32
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
public function connect_on(
|
|
196
|
+
runtime: aio.Runtime,
|
|
197
|
+
local_address: net.SocketAddress,
|
|
198
|
+
remote_address: net.SocketAddress,
|
|
199
|
+
config: Config
|
|
200
|
+
) -> Result[Connection, net.Error]:
|
|
201
|
+
let channel_config = channel_config_for(config)
|
|
202
|
+
let channel_result = chan.connect_on(runtime, local_address, remote_address, channel_config)
|
|
203
|
+
match channel_result:
|
|
204
|
+
Result.failure as payload:
|
|
205
|
+
return Result[Connection, net.Error].failure(error = net.Error(
|
|
206
|
+
code = payload.error.code,
|
|
207
|
+
message = payload.error.message
|
|
208
|
+
))
|
|
209
|
+
Result.success as payload:
|
|
210
|
+
let conn = Connection(
|
|
211
|
+
channel = payload.value,
|
|
212
|
+
state = ConnectionState.disconnected,
|
|
213
|
+
protocol_version = config.protocol_version,
|
|
214
|
+
heartbeat_interval = config.heartbeat_interval,
|
|
215
|
+
heartbeat_timeout = config.heartbeat_timeout,
|
|
216
|
+
frame_since_last_recv = 0,
|
|
217
|
+
ping_rtt_frames = 0,
|
|
218
|
+
last_heartbeat_sent = 0,
|
|
219
|
+
pending_recv = Option[ChanMessageTask].none,
|
|
220
|
+
event_queue = deque.Deque[PeerEvent].create(),
|
|
221
|
+
outgoing = deque.Deque[OutgoingMessage].create()
|
|
222
|
+
)
|
|
223
|
+
return Result[Connection, net.Error].success(value = conn)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
public async function connect(
|
|
227
|
+
local_address: net.SocketAddress,
|
|
228
|
+
remote_address: net.SocketAddress,
|
|
229
|
+
config: Config
|
|
230
|
+
) -> Result[Connection, net.Error]:
|
|
231
|
+
match connect_on(aio.current_runtime(), local_address, remote_address, config):
|
|
232
|
+
Result.failure as p:
|
|
233
|
+
return Result[Connection, net.Error].failure(error = p.error)
|
|
234
|
+
Result.success as p:
|
|
235
|
+
var conn = p.value
|
|
236
|
+
match await conn.connect_to_peer():
|
|
237
|
+
Result.failure as cp:
|
|
238
|
+
conn.release()
|
|
239
|
+
return Result[Connection, net.Error].failure(error = cp.error)
|
|
240
|
+
Result.success:
|
|
241
|
+
return Result[Connection, net.Error].success(value = conn)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
public function listen_on(
|
|
245
|
+
runtime: aio.Runtime,
|
|
246
|
+
local_address: net.SocketAddress,
|
|
247
|
+
config: Config
|
|
248
|
+
) -> Result[Session, net.Error]:
|
|
249
|
+
let channel_config = channel_config_for(config)
|
|
250
|
+
let channel_result = chan.listen_on(runtime, local_address, channel_config)
|
|
251
|
+
match channel_result:
|
|
252
|
+
Result.failure as payload:
|
|
253
|
+
return Result[Session, net.Error].failure(error = net.Error(
|
|
254
|
+
code = payload.error.code,
|
|
255
|
+
message = payload.error.message
|
|
256
|
+
))
|
|
257
|
+
Result.success as payload:
|
|
258
|
+
let session = Session(
|
|
259
|
+
host = payload.value,
|
|
260
|
+
config = config,
|
|
261
|
+
next_peer_id = 1,
|
|
262
|
+
peers = vec.Vec[PeerConnection].create(),
|
|
263
|
+
pending_recv = Option[ChanHostMessageTask].none,
|
|
264
|
+
event_queue = deque.Deque[PeerEvent].create(),
|
|
265
|
+
outgoing = deque.Deque[OutgoingMessage].create()
|
|
266
|
+
)
|
|
267
|
+
return Result[Session, net.Error].success(value = session)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
public function listen(local_address: net.SocketAddress, config: Config) -> Result[Session, net.Error]:
|
|
271
|
+
return listen_on(aio.current_runtime(), local_address, config)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
extending Connection:
|
|
275
|
+
public editable function release() -> void:
|
|
276
|
+
if this.pending_recv.is_some():
|
|
277
|
+
let task = this.pending_recv.unwrap()
|
|
278
|
+
let raw_frame = unsafe: reinterpret[ptr_int](task.frame)
|
|
279
|
+
if raw_frame != 0:
|
|
280
|
+
task.release(task.frame)
|
|
281
|
+
this.pending_recv = Option[ChanMessageTask].none
|
|
282
|
+
drain_release_outgoing_conn(ref_of(this))
|
|
283
|
+
this.outgoing.release()
|
|
284
|
+
drain_release_events(ref_of(this.event_queue))
|
|
285
|
+
this.event_queue.release()
|
|
286
|
+
this.channel.release()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
public function state() -> ConnectionState:
|
|
290
|
+
return this.state
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
public function local_address() -> Result[net.SocketAddress, net.Error]:
|
|
294
|
+
return this.channel.local_address()
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
public function peer_address() -> Result[net.SocketAddress, net.Error]:
|
|
298
|
+
return this.channel.peer_address()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
public function ping_rtt_frames() -> uint:
|
|
302
|
+
return this.ping_rtt_frames
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
public editable function start_recv() -> void:
|
|
306
|
+
start_recv_if_idle(ref_of(this))
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
public async editable function tick(frame: uint) -> Result[bool, net.Error]:
|
|
310
|
+
start_recv_if_idle(ref_of(this))
|
|
311
|
+
|
|
312
|
+
if this.state == ConnectionState.connected:
|
|
313
|
+
if frame >= this.last_heartbeat_sent + this.heartbeat_interval:
|
|
314
|
+
var hb = build_heartbeat(frame)
|
|
315
|
+
defer hb.release()
|
|
316
|
+
let send_result = await this.channel.send(hb.as_span())
|
|
317
|
+
match send_result:
|
|
318
|
+
Result.failure:
|
|
319
|
+
pass
|
|
320
|
+
Result.success:
|
|
321
|
+
pass
|
|
322
|
+
this.last_heartbeat_sent = frame
|
|
323
|
+
|
|
324
|
+
this.frame_since_last_recv += 1
|
|
325
|
+
if this.frame_since_last_recv >= this.heartbeat_timeout:
|
|
326
|
+
this.state = ConnectionState.disconnected
|
|
327
|
+
|
|
328
|
+
let channel_tick_result = await this.channel.tick(frame)
|
|
329
|
+
match channel_tick_result:
|
|
330
|
+
Result.failure:
|
|
331
|
+
pass
|
|
332
|
+
Result.success:
|
|
333
|
+
pass
|
|
334
|
+
|
|
335
|
+
await aio.sleep(1)
|
|
336
|
+
|
|
337
|
+
process_pending_recv_conn(ref_of(this))
|
|
338
|
+
await drain_outgoing_conn(ref_of(this))
|
|
339
|
+
|
|
340
|
+
return Result[bool, net.Error].success(value = this.state == ConnectionState.connected)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
public editable function try_recv() -> Option[PeerEvent]:
|
|
344
|
+
return this.event_queue.pop_front()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
public async editable function recv() -> Result[Option[PeerEvent], net.Error]:
|
|
348
|
+
while true:
|
|
349
|
+
let ev = this.try_recv()
|
|
350
|
+
match ev:
|
|
351
|
+
Option.some as payload:
|
|
352
|
+
return Result[Option[PeerEvent], net.Error].success(
|
|
353
|
+
value = Option[PeerEvent].some(value = payload.value)
|
|
354
|
+
)
|
|
355
|
+
Option.none:
|
|
356
|
+
process_pending_recv_conn(ref_of(this))
|
|
357
|
+
await drain_outgoing_conn(ref_of(this))
|
|
358
|
+
let ready = this.try_recv()
|
|
359
|
+
match ready:
|
|
360
|
+
Option.some as ready_payload:
|
|
361
|
+
return Result[Option[PeerEvent], net.Error].success(
|
|
362
|
+
value = Option[PeerEvent].some(value = ready_payload.value)
|
|
363
|
+
)
|
|
364
|
+
Option.none:
|
|
365
|
+
let recv_result = await this.channel.recv()
|
|
366
|
+
match recv_result:
|
|
367
|
+
Result.failure as payload:
|
|
368
|
+
return Result[Option[PeerEvent], net.Error].failure(
|
|
369
|
+
error = net.Error(code = payload.error.code, message = payload.error.message)
|
|
370
|
+
)
|
|
371
|
+
Result.success as recv_payload:
|
|
372
|
+
let msg_opt = recv_payload.value
|
|
373
|
+
match msg_opt:
|
|
374
|
+
Option.none:
|
|
375
|
+
pass
|
|
376
|
+
Option.some as msg_payload:
|
|
377
|
+
var msg = msg_payload.value
|
|
378
|
+
handle_incoming_conn(ref_of(this), ref_of(msg))
|
|
379
|
+
await drain_outgoing_conn(ref_of(this))
|
|
380
|
+
let ev2 = this.try_recv()
|
|
381
|
+
match ev2:
|
|
382
|
+
Option.some as p2:
|
|
383
|
+
return Result[Option[PeerEvent], net.Error].success(
|
|
384
|
+
value = Option[PeerEvent].some(value = p2.value)
|
|
385
|
+
)
|
|
386
|
+
Option.none:
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
public async editable function send(data: span[ubyte]) -> Result[bool, net.Error]:
|
|
391
|
+
if this.state != ConnectionState.connected:
|
|
392
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
393
|
+
code = -2,
|
|
394
|
+
message = string.String.from_str("session not connected")
|
|
395
|
+
))
|
|
396
|
+
|
|
397
|
+
var framed = build_user_data(data)
|
|
398
|
+
defer framed.release()
|
|
399
|
+
let send_result = await this.channel.send(framed.as_span())
|
|
400
|
+
match send_result:
|
|
401
|
+
Result.failure as payload:
|
|
402
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
403
|
+
code = payload.error.code,
|
|
404
|
+
message = payload.error.message
|
|
405
|
+
))
|
|
406
|
+
Result.success:
|
|
407
|
+
return Result[bool, net.Error].success(value = true)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
public async editable function send_reliable(data: span[ubyte], frame: uint) -> Result[bool, net.Error]:
|
|
411
|
+
if this.state != ConnectionState.connected:
|
|
412
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
413
|
+
code = -2,
|
|
414
|
+
message = string.String.from_str("session not connected")
|
|
415
|
+
))
|
|
416
|
+
|
|
417
|
+
var framed = build_user_data(data)
|
|
418
|
+
defer framed.release()
|
|
419
|
+
let send_result = await this.channel.send_reliable(framed.as_span(), frame)
|
|
420
|
+
match send_result:
|
|
421
|
+
Result.failure as payload:
|
|
422
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
423
|
+
code = payload.error.code,
|
|
424
|
+
message = payload.error.message
|
|
425
|
+
))
|
|
426
|
+
Result.success:
|
|
427
|
+
return Result[bool, net.Error].success(value = true)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
public async editable function connect_to_peer() -> Result[bool, net.Error]:
|
|
431
|
+
if this.state != ConnectionState.disconnected:
|
|
432
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
433
|
+
code = -3,
|
|
434
|
+
message = string.String.from_str("session already connected or connecting")
|
|
435
|
+
))
|
|
436
|
+
|
|
437
|
+
this.state = ConnectionState.connecting
|
|
438
|
+
|
|
439
|
+
var key: ulong = 0
|
|
440
|
+
match generate_handshake_key():
|
|
441
|
+
Result.failure as kp:
|
|
442
|
+
this.state = ConnectionState.disconnected
|
|
443
|
+
return Result[bool, net.Error].failure(error = kp.error)
|
|
444
|
+
Result.success as ksp:
|
|
445
|
+
key = ksp.value
|
|
446
|
+
var req = build_connect_request(this.protocol_version, key)
|
|
447
|
+
defer req.release()
|
|
448
|
+
let send_result = await this.channel.send_reliable(req.as_span(), this.last_heartbeat_sent)
|
|
449
|
+
match send_result:
|
|
450
|
+
Result.failure as send_payload:
|
|
451
|
+
this.state = ConnectionState.disconnected
|
|
452
|
+
return Result[bool, net.Error].failure(error = send_payload.error)
|
|
453
|
+
Result.success:
|
|
454
|
+
this.frame_since_last_recv = 0
|
|
455
|
+
return Result[bool, net.Error].success(value = true)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
public async editable function disconnect_peer() -> Result[bool, net.Error]:
|
|
459
|
+
if this.state != ConnectionState.connected:
|
|
460
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
461
|
+
code = -4,
|
|
462
|
+
message = string.String.from_str("session not connected")
|
|
463
|
+
))
|
|
464
|
+
|
|
465
|
+
var d = build_disconnect(disconnect_reason_local)
|
|
466
|
+
defer d.release()
|
|
467
|
+
let send_result = await this.channel.send_reliable(d.as_span(), 0)
|
|
468
|
+
this.state = ConnectionState.disconnected
|
|
469
|
+
match send_result:
|
|
470
|
+
Result.failure:
|
|
471
|
+
return Result[bool, net.Error].success(value = true)
|
|
472
|
+
Result.success:
|
|
473
|
+
return Result[bool, net.Error].success(value = true)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
function start_recv_if_idle(conn: ref[Connection]) -> void:
|
|
477
|
+
match conn.pending_recv:
|
|
478
|
+
Option.some:
|
|
479
|
+
return
|
|
480
|
+
Option.none:
|
|
481
|
+
conn.pending_recv = Option[ChanMessageTask].some(
|
|
482
|
+
value = conn.channel.recv()
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
function process_pending_recv_conn(conn: ref[Connection]) -> void:
|
|
487
|
+
match conn.pending_recv:
|
|
488
|
+
Option.none:
|
|
489
|
+
return
|
|
490
|
+
Option.some as task_opt:
|
|
491
|
+
let task = task_opt.value
|
|
492
|
+
if not aio.completed(task):
|
|
493
|
+
return
|
|
494
|
+
let result = aio.result(task)
|
|
495
|
+
conn.pending_recv = Option[ChanMessageTask].none
|
|
496
|
+
start_recv_if_idle(conn)
|
|
497
|
+
match result:
|
|
498
|
+
Result.failure:
|
|
499
|
+
return
|
|
500
|
+
Result.success as result_payload:
|
|
501
|
+
let msg_opt = result_payload.value
|
|
502
|
+
match msg_opt:
|
|
503
|
+
Option.none:
|
|
504
|
+
pass
|
|
505
|
+
Option.some as msg_payload:
|
|
506
|
+
var msg = msg_payload.value
|
|
507
|
+
handle_incoming_conn(conn, ref_of(msg))
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
function handle_incoming_conn(conn: ref[Connection], msg: ref[chan.Message]) -> void:
|
|
511
|
+
let span = msg.payload.as_span()
|
|
512
|
+
if span.len == 0:
|
|
513
|
+
msg.release()
|
|
514
|
+
return
|
|
515
|
+
|
|
516
|
+
let tag = unsafe: read(span.data)
|
|
517
|
+
|
|
518
|
+
if tag == msg_type_user:
|
|
519
|
+
conn.frame_since_last_recv = 0
|
|
520
|
+
let payload_len = span.len - 1
|
|
521
|
+
var payload = bytes.Bytes.empty()
|
|
522
|
+
if payload_len > 0:
|
|
523
|
+
unsafe:
|
|
524
|
+
payload = bytes.Bytes.copy(span[ubyte](data = span.data + 1, len = payload_len))
|
|
525
|
+
conn.event_queue.push_back(PeerEvent(
|
|
526
|
+
kind = PeerEventKind.user_data,
|
|
527
|
+
peer_id = 0,
|
|
528
|
+
payload = payload,
|
|
529
|
+
reliable = msg.reliable,
|
|
530
|
+
reason = 0
|
|
531
|
+
))
|
|
532
|
+
msg.release()
|
|
533
|
+
return
|
|
534
|
+
|
|
535
|
+
if tag == msg_type_heartbeat:
|
|
536
|
+
conn.frame_since_last_recv = 0
|
|
537
|
+
let hb_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
538
|
+
if hb_span.len >= 4:
|
|
539
|
+
let echoed = decode_u32_at(hb_span, 0)
|
|
540
|
+
var ack = build_heartbeat_ack(echoed)
|
|
541
|
+
let peer_addr_result = conn.channel.peer_address()
|
|
542
|
+
match peer_addr_result:
|
|
543
|
+
Result.success as peer_addr:
|
|
544
|
+
conn.outgoing.push_back(OutgoingMessage(
|
|
545
|
+
address = peer_addr.value,
|
|
546
|
+
payload = ack,
|
|
547
|
+
reliable = false
|
|
548
|
+
))
|
|
549
|
+
Result.failure:
|
|
550
|
+
ack.release()
|
|
551
|
+
msg.release()
|
|
552
|
+
return
|
|
553
|
+
|
|
554
|
+
if tag == msg_type_heartbeat_ack:
|
|
555
|
+
conn.frame_since_last_recv = 0
|
|
556
|
+
let echo_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
557
|
+
if echo_span.len >= 4:
|
|
558
|
+
let echoed = decode_u32_at(echo_span, 0)
|
|
559
|
+
if conn.last_heartbeat_sent > echoed:
|
|
560
|
+
conn.ping_rtt_frames = conn.last_heartbeat_sent - echoed
|
|
561
|
+
msg.release()
|
|
562
|
+
return
|
|
563
|
+
|
|
564
|
+
if tag == msg_type_connect_accept:
|
|
565
|
+
conn.frame_since_last_recv = 0
|
|
566
|
+
conn.state = ConnectionState.connected
|
|
567
|
+
msg.release()
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
if tag == msg_type_connect_reject:
|
|
571
|
+
conn.state = ConnectionState.disconnected
|
|
572
|
+
msg.release()
|
|
573
|
+
return
|
|
574
|
+
|
|
575
|
+
if tag == msg_type_disconnect:
|
|
576
|
+
conn.state = ConnectionState.disconnected
|
|
577
|
+
msg.release()
|
|
578
|
+
return
|
|
579
|
+
|
|
580
|
+
msg.release()
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
async function drain_outgoing_conn(conn: ref[Connection]) -> Result[bool, net.Error]:
|
|
584
|
+
while true:
|
|
585
|
+
let msg = conn.outgoing.pop_front()
|
|
586
|
+
match msg:
|
|
587
|
+
Option.none:
|
|
588
|
+
return Result[bool, net.Error].success(value = true)
|
|
589
|
+
Option.some as payload:
|
|
590
|
+
var outgoing = payload.value
|
|
591
|
+
defer outgoing.release()
|
|
592
|
+
if outgoing.reliable:
|
|
593
|
+
let _ = await conn.channel.send_reliable(
|
|
594
|
+
outgoing.payload.as_span(),
|
|
595
|
+
0
|
|
596
|
+
)
|
|
597
|
+
else:
|
|
598
|
+
let _ = await conn.channel.send(
|
|
599
|
+
outgoing.payload.as_span()
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
extending OutgoingMessage:
|
|
604
|
+
public editable function release() -> void:
|
|
605
|
+
this.address.release()
|
|
606
|
+
this.payload.release()
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
extending Session:
|
|
610
|
+
public editable function release() -> void:
|
|
611
|
+
match this.pending_recv:
|
|
612
|
+
Option.some as task_opt:
|
|
613
|
+
let task = task_opt.value
|
|
614
|
+
let raw_frame = unsafe: reinterpret[ptr_int](task.frame)
|
|
615
|
+
if raw_frame != 0:
|
|
616
|
+
task.release(task.frame)
|
|
617
|
+
Option.none:
|
|
618
|
+
pass
|
|
619
|
+
this.pending_recv = Option[ChanHostMessageTask].none
|
|
620
|
+
drain_release_outgoing(ref_of(this))
|
|
621
|
+
this.outgoing.release()
|
|
622
|
+
drain_release_events(ref_of(this.event_queue))
|
|
623
|
+
this.event_queue.release()
|
|
624
|
+
this.peers.release()
|
|
625
|
+
this.host.release()
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
public function local_address() -> Result[net.SocketAddress, net.Error]:
|
|
629
|
+
return this.host.local_address()
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
public function peer_count() -> ptr_uint:
|
|
633
|
+
return this.peers.len()
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
public editable function start_recv() -> void:
|
|
637
|
+
start_recv_if_idle_session(ref_of(this))
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
public async editable function tick(frame: uint) -> Result[bool, net.Error]:
|
|
641
|
+
start_recv_if_idle_session(ref_of(this))
|
|
642
|
+
|
|
643
|
+
var peer_index: ptr_uint = 0
|
|
644
|
+
while peer_index < this.peers.len():
|
|
645
|
+
let peer_ptr = this.peers.get(peer_index) else:
|
|
646
|
+
fatal(c"session peer missing storage")
|
|
647
|
+
|
|
648
|
+
if unsafe: read(peer_ptr).channel_state == ConnectionState.connected:
|
|
649
|
+
if frame >= unsafe: read(peer_ptr).last_heartbeat_sent + this.config.heartbeat_interval:
|
|
650
|
+
var hb = build_heartbeat(frame)
|
|
651
|
+
defer hb.release()
|
|
652
|
+
let peer_id = unsafe: read(peer_ptr).peer_id
|
|
653
|
+
let peer_address_result = find_peer_address(ref_of(this), peer_id)
|
|
654
|
+
match peer_address_result:
|
|
655
|
+
Result.success as addr_payload:
|
|
656
|
+
var addr = addr_payload.value
|
|
657
|
+
defer addr.release()
|
|
658
|
+
let send_result = await this.host.send(addr, hb.as_span())
|
|
659
|
+
match send_result:
|
|
660
|
+
Result.failure:
|
|
661
|
+
pass
|
|
662
|
+
Result.success:
|
|
663
|
+
pass
|
|
664
|
+
Result.failure:
|
|
665
|
+
pass
|
|
666
|
+
unsafe: read(peer_ptr).last_heartbeat_sent = frame
|
|
667
|
+
|
|
668
|
+
unsafe: read(peer_ptr).frame_since_last_recv += 1
|
|
669
|
+
if unsafe: read(peer_ptr).frame_since_last_recv >= this.config.heartbeat_timeout:
|
|
670
|
+
unsafe: read(peer_ptr).channel_state = ConnectionState.disconnected
|
|
671
|
+
this.event_queue.push_back(PeerEvent(
|
|
672
|
+
kind = PeerEventKind.peer_left,
|
|
673
|
+
peer_id = unsafe: read(peer_ptr).peer_id,
|
|
674
|
+
payload = bytes.Bytes.empty(),
|
|
675
|
+
reliable = false,
|
|
676
|
+
reason = disconnect_reason_timeout
|
|
677
|
+
))
|
|
678
|
+
|
|
679
|
+
peer_index += 1
|
|
680
|
+
|
|
681
|
+
let host_tick_result = await this.host.tick(frame)
|
|
682
|
+
match host_tick_result:
|
|
683
|
+
Result.failure:
|
|
684
|
+
pass
|
|
685
|
+
Result.success:
|
|
686
|
+
pass
|
|
687
|
+
|
|
688
|
+
await aio.sleep(1)
|
|
689
|
+
|
|
690
|
+
process_pending_recv_session(ref_of(this))
|
|
691
|
+
await drain_outgoing_session(ref_of(this))
|
|
692
|
+
|
|
693
|
+
let connected_count = connected_peer_count(ref_of(this))
|
|
694
|
+
return Result[bool, net.Error].success(value = connected_count > 0)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
public editable function try_recv() -> Option[PeerEvent]:
|
|
698
|
+
return this.event_queue.pop_front()
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
public async editable function recv() -> Result[Option[PeerEvent], net.Error]:
|
|
702
|
+
while true:
|
|
703
|
+
let ev = this.try_recv()
|
|
704
|
+
match ev:
|
|
705
|
+
Option.some as payload:
|
|
706
|
+
return Result[Option[PeerEvent], net.Error].success(
|
|
707
|
+
value = Option[PeerEvent].some(value = payload.value)
|
|
708
|
+
)
|
|
709
|
+
Option.none:
|
|
710
|
+
process_pending_recv_session(ref_of(this))
|
|
711
|
+
await drain_outgoing_session(ref_of(this))
|
|
712
|
+
let ready = this.try_recv()
|
|
713
|
+
match ready:
|
|
714
|
+
Option.some as ready_payload:
|
|
715
|
+
return Result[Option[PeerEvent], net.Error].success(
|
|
716
|
+
value = Option[PeerEvent].some(value = ready_payload.value)
|
|
717
|
+
)
|
|
718
|
+
Option.none:
|
|
719
|
+
let recv_result = await this.host.recv()
|
|
720
|
+
match recv_result:
|
|
721
|
+
Result.failure as payload:
|
|
722
|
+
return Result[Option[PeerEvent], net.Error].failure(
|
|
723
|
+
error = net.Error(code = payload.error.code, message = payload.error.message)
|
|
724
|
+
)
|
|
725
|
+
Result.success as recv_payload:
|
|
726
|
+
let msg_opt = recv_payload.value
|
|
727
|
+
match msg_opt:
|
|
728
|
+
Option.none:
|
|
729
|
+
pass
|
|
730
|
+
Option.some as msg_payload:
|
|
731
|
+
var msg = msg_payload.value
|
|
732
|
+
handle_incoming_session(ref_of(this), ref_of(msg))
|
|
733
|
+
await drain_outgoing_session(ref_of(this))
|
|
734
|
+
let ev2 = this.try_recv()
|
|
735
|
+
match ev2:
|
|
736
|
+
Option.some as p2:
|
|
737
|
+
return Result[Option[PeerEvent], net.Error].success(
|
|
738
|
+
value = Option[PeerEvent].some(value = p2.value)
|
|
739
|
+
)
|
|
740
|
+
Option.none:
|
|
741
|
+
pass
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
public async editable function send(peer_id: uint, data: span[ubyte]) -> Result[bool, net.Error]:
|
|
745
|
+
let peer_address_result = find_peer_address(ref_of(this), peer_id)
|
|
746
|
+
match peer_address_result:
|
|
747
|
+
Result.failure as payload:
|
|
748
|
+
return Result[bool, net.Error].failure(error = payload.error)
|
|
749
|
+
Result.success as payload:
|
|
750
|
+
var address = payload.value
|
|
751
|
+
defer address.release()
|
|
752
|
+
var framed = build_user_data(data)
|
|
753
|
+
defer framed.release()
|
|
754
|
+
let send_result = await this.host.send(address, framed.as_span())
|
|
755
|
+
match send_result:
|
|
756
|
+
Result.failure as send_payload:
|
|
757
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
758
|
+
code = send_payload.error.code,
|
|
759
|
+
message = send_payload.error.message
|
|
760
|
+
))
|
|
761
|
+
Result.success:
|
|
762
|
+
return Result[bool, net.Error].success(value = true)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
public async editable function send_reliable(
|
|
766
|
+
peer_id: uint,
|
|
767
|
+
data: span[ubyte],
|
|
768
|
+
frame: uint
|
|
769
|
+
) -> Result[bool, net.Error]:
|
|
770
|
+
let peer_address_result = find_peer_address(ref_of(this), peer_id)
|
|
771
|
+
match peer_address_result:
|
|
772
|
+
Result.failure as payload:
|
|
773
|
+
return Result[bool, net.Error].failure(error = payload.error)
|
|
774
|
+
Result.success as payload:
|
|
775
|
+
var address = payload.value
|
|
776
|
+
defer address.release()
|
|
777
|
+
var framed = build_user_data(data)
|
|
778
|
+
defer framed.release()
|
|
779
|
+
let send_result = await this.host.send_reliable(address, framed.as_span(), frame)
|
|
780
|
+
match send_result:
|
|
781
|
+
Result.failure as send_payload:
|
|
782
|
+
return Result[bool, net.Error].failure(error = net.Error(
|
|
783
|
+
code = send_payload.error.code,
|
|
784
|
+
message = send_payload.error.message
|
|
785
|
+
))
|
|
786
|
+
Result.success:
|
|
787
|
+
return Result[bool, net.Error].success(value = true)
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
public async editable function kick(peer_id: uint, reason: ubyte) -> Result[bool, net.Error]:
|
|
791
|
+
let peer_address_result = find_peer_address(ref_of(this), peer_id)
|
|
792
|
+
match peer_address_result:
|
|
793
|
+
Result.failure as payload:
|
|
794
|
+
return Result[bool, net.Error].failure(error = payload.error)
|
|
795
|
+
Result.success as payload:
|
|
796
|
+
var address = payload.value
|
|
797
|
+
defer address.release()
|
|
798
|
+
var d = build_disconnect(reason)
|
|
799
|
+
defer d.release()
|
|
800
|
+
let send_result = await this.host.send_reliable(address, d.as_span(), 0)
|
|
801
|
+
match send_result:
|
|
802
|
+
Result.failure:
|
|
803
|
+
pass
|
|
804
|
+
Result.success:
|
|
805
|
+
pass
|
|
806
|
+
mark_peer_disconnected(ref_of(this), peer_id)
|
|
807
|
+
this.event_queue.push_back(PeerEvent(
|
|
808
|
+
kind = PeerEventKind.peer_left,
|
|
809
|
+
peer_id = peer_id,
|
|
810
|
+
reason = reason
|
|
811
|
+
))
|
|
812
|
+
return Result[bool, net.Error].success(value = true)
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
function find_peer_address(session: ref[Session], peer_id: uint) -> Result[net.SocketAddress, net.Error]:
|
|
816
|
+
var peer_index: ptr_uint = 0
|
|
817
|
+
while peer_index < session.peers.len():
|
|
818
|
+
let peer_ptr = session.peers.get(peer_index) else:
|
|
819
|
+
fatal(c"session peer missing storage in address lookup")
|
|
820
|
+
|
|
821
|
+
if unsafe: read(peer_ptr).peer_id == peer_id:
|
|
822
|
+
var host_peer_index: ptr_uint = 0
|
|
823
|
+
let host_peers = session.host.peers
|
|
824
|
+
while host_peer_index < host_peers.len():
|
|
825
|
+
let host_peer_ptr = host_peers.get(host_peer_index) else:
|
|
826
|
+
fatal(c"session host peer missing storage")
|
|
827
|
+
if host_peer_index == peer_index:
|
|
828
|
+
let copy_result = unsafe: read(host_peer_ptr).address.copy()
|
|
829
|
+
match copy_result:
|
|
830
|
+
Result.failure as copy_err:
|
|
831
|
+
let copy_message = copy_err.error.message.as_str()
|
|
832
|
+
return Result[net.SocketAddress, net.Error].failure(
|
|
833
|
+
error = net.Error(code = -5, message = string.String.from_str(copy_message))
|
|
834
|
+
)
|
|
835
|
+
Result.success as addr_payload:
|
|
836
|
+
return Result[net.SocketAddress, net.Error].success(value = addr_payload.value)
|
|
837
|
+
host_peer_index += 1
|
|
838
|
+
peer_index += 1
|
|
839
|
+
|
|
840
|
+
return Result[net.SocketAddress, net.Error].failure(error = net.Error(
|
|
841
|
+
code = -6,
|
|
842
|
+
message = string.String.from_str("session peer not found")
|
|
843
|
+
))
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
function start_recv_if_idle_session(session: ref[Session]) -> void:
|
|
847
|
+
match session.pending_recv:
|
|
848
|
+
Option.some:
|
|
849
|
+
return
|
|
850
|
+
Option.none:
|
|
851
|
+
session.pending_recv = Option[ChanHostMessageTask].some(
|
|
852
|
+
value = session.host.recv()
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
function process_pending_recv_session(session: ref[Session]) -> void:
|
|
857
|
+
match session.pending_recv:
|
|
858
|
+
Option.none:
|
|
859
|
+
return
|
|
860
|
+
Option.some as task_opt:
|
|
861
|
+
let task = task_opt.value
|
|
862
|
+
if not aio.completed(task):
|
|
863
|
+
return
|
|
864
|
+
let result = aio.result(task)
|
|
865
|
+
session.pending_recv = Option[ChanHostMessageTask].none
|
|
866
|
+
start_recv_if_idle_session(session)
|
|
867
|
+
match result:
|
|
868
|
+
Result.failure:
|
|
869
|
+
return
|
|
870
|
+
Result.success as result_payload:
|
|
871
|
+
let msg_opt = result_payload.value
|
|
872
|
+
match msg_opt:
|
|
873
|
+
Option.none:
|
|
874
|
+
pass
|
|
875
|
+
Option.some as msg_payload:
|
|
876
|
+
var msg = msg_payload.value
|
|
877
|
+
handle_incoming_session(session, ref_of(msg))
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
function handle_incoming_session(session: ref[Session], msg: ref[chan.HostMessage]) -> void:
|
|
881
|
+
let span = msg.payload.as_span()
|
|
882
|
+
if span.len == 0:
|
|
883
|
+
msg.release()
|
|
884
|
+
return
|
|
885
|
+
|
|
886
|
+
let tag = unsafe: read(span.data)
|
|
887
|
+
|
|
888
|
+
if tag == msg_type_connect_request:
|
|
889
|
+
handle_connect_request(session, msg, span)
|
|
890
|
+
return
|
|
891
|
+
|
|
892
|
+
if tag == msg_type_user:
|
|
893
|
+
let peer_result = find_peer_by_address(session, msg.source)
|
|
894
|
+
match peer_result:
|
|
895
|
+
Result.failure:
|
|
896
|
+
msg.release()
|
|
897
|
+
return
|
|
898
|
+
Result.success as peer_payload:
|
|
899
|
+
let peer_id = peer_payload.value
|
|
900
|
+
update_peer_last_recv(session, peer_id)
|
|
901
|
+
let payload_len = span.len - 1
|
|
902
|
+
var payload = bytes.Bytes.empty()
|
|
903
|
+
if payload_len > 0:
|
|
904
|
+
unsafe:
|
|
905
|
+
payload = bytes.Bytes.copy(span[ubyte](data = span.data + 1, len = payload_len))
|
|
906
|
+
session.event_queue.push_back(PeerEvent(
|
|
907
|
+
kind = PeerEventKind.user_data,
|
|
908
|
+
peer_id = peer_id,
|
|
909
|
+
payload = payload,
|
|
910
|
+
reliable = msg.reliable,
|
|
911
|
+
reason = 0
|
|
912
|
+
))
|
|
913
|
+
msg.release()
|
|
914
|
+
return
|
|
915
|
+
|
|
916
|
+
if tag == msg_type_heartbeat:
|
|
917
|
+
let peer_result = find_peer_by_address(session, msg.source)
|
|
918
|
+
match peer_result:
|
|
919
|
+
Result.success as peer_payload:
|
|
920
|
+
update_peer_last_recv(session, peer_payload.value)
|
|
921
|
+
let hb_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
922
|
+
if hb_span.len >= 4:
|
|
923
|
+
let echoed = decode_u32_at(hb_span, 0)
|
|
924
|
+
var ack = build_heartbeat_ack(echoed)
|
|
925
|
+
let addr_result = msg.source.copy()
|
|
926
|
+
match addr_result:
|
|
927
|
+
Result.success as addr_payload:
|
|
928
|
+
session.outgoing.push_back(OutgoingMessage(
|
|
929
|
+
address = addr_payload.value,
|
|
930
|
+
payload = ack,
|
|
931
|
+
reliable = false
|
|
932
|
+
))
|
|
933
|
+
Result.failure:
|
|
934
|
+
ack.release()
|
|
935
|
+
Result.failure:
|
|
936
|
+
pass
|
|
937
|
+
msg.release()
|
|
938
|
+
return
|
|
939
|
+
|
|
940
|
+
if tag == msg_type_heartbeat_ack:
|
|
941
|
+
let peer_result = find_peer_by_address(session, msg.source)
|
|
942
|
+
match peer_result:
|
|
943
|
+
Result.success as peer_payload:
|
|
944
|
+
update_peer_last_recv(session, peer_payload.value)
|
|
945
|
+
mark_peer_heartbeat_ack(session, peer_payload.value)
|
|
946
|
+
Result.failure:
|
|
947
|
+
pass
|
|
948
|
+
msg.release()
|
|
949
|
+
return
|
|
950
|
+
|
|
951
|
+
if tag == msg_type_disconnect:
|
|
952
|
+
let peer_result = find_peer_by_address(session, msg.source)
|
|
953
|
+
match peer_result:
|
|
954
|
+
Result.success as peer_payload:
|
|
955
|
+
let peer_id = peer_payload.value
|
|
956
|
+
mark_peer_disconnected(session, peer_id)
|
|
957
|
+
session.event_queue.push_back(PeerEvent(
|
|
958
|
+
kind = PeerEventKind.peer_left,
|
|
959
|
+
peer_id = peer_id,
|
|
960
|
+
reason = disconnect_reason_remote
|
|
961
|
+
))
|
|
962
|
+
Result.failure:
|
|
963
|
+
pass
|
|
964
|
+
msg.release()
|
|
965
|
+
return
|
|
966
|
+
|
|
967
|
+
msg.release()
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
function handle_connect_request(session: ref[Session], msg: ref[chan.HostMessage], span: span[ubyte]) -> void:
|
|
971
|
+
let req_span = unsafe: span[ubyte](data = span.data + 1, len = span.len - 1)
|
|
972
|
+
if req_span.len < 4:
|
|
973
|
+
msg.release()
|
|
974
|
+
return
|
|
975
|
+
|
|
976
|
+
let client_version = decode_u32_at(req_span, 0)
|
|
977
|
+
if client_version != session.config.protocol_version:
|
|
978
|
+
var reject = build_connect_reject(reject_reason_version)
|
|
979
|
+
let addr_result = msg.source.copy()
|
|
980
|
+
match addr_result:
|
|
981
|
+
Result.success as addr_payload:
|
|
982
|
+
session.outgoing.push_back(OutgoingMessage(
|
|
983
|
+
address = addr_payload.value,
|
|
984
|
+
payload = reject,
|
|
985
|
+
reliable = true
|
|
986
|
+
))
|
|
987
|
+
Result.failure:
|
|
988
|
+
reject.release()
|
|
989
|
+
msg.release()
|
|
990
|
+
return
|
|
991
|
+
|
|
992
|
+
let peer_id = session.next_peer_id
|
|
993
|
+
session.next_peer_id += 1
|
|
994
|
+
|
|
995
|
+
var accept = build_connect_accept(peer_id)
|
|
996
|
+
let addr_result = msg.source.copy()
|
|
997
|
+
match addr_result:
|
|
998
|
+
Result.success as addr_payload:
|
|
999
|
+
session.outgoing.push_back(OutgoingMessage(
|
|
1000
|
+
address = addr_payload.value,
|
|
1001
|
+
payload = accept,
|
|
1002
|
+
reliable = true
|
|
1003
|
+
))
|
|
1004
|
+
Result.failure:
|
|
1005
|
+
accept.release()
|
|
1006
|
+
msg.release()
|
|
1007
|
+
|
|
1008
|
+
session.peers.push(PeerConnection(
|
|
1009
|
+
peer_id = peer_id,
|
|
1010
|
+
channel_state = ConnectionState.connected,
|
|
1011
|
+
frame_since_last_recv = 0,
|
|
1012
|
+
last_heartbeat_sent = 0,
|
|
1013
|
+
heartbeat_ack_received = false
|
|
1014
|
+
))
|
|
1015
|
+
|
|
1016
|
+
session.event_queue.push_back(PeerEvent(
|
|
1017
|
+
kind = PeerEventKind.peer_joined,
|
|
1018
|
+
peer_id = peer_id
|
|
1019
|
+
))
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
function find_peer_by_address(session: ref[Session], address: net.SocketAddress) -> Result[uint, net.Error]:
|
|
1023
|
+
var index: ptr_uint = 0
|
|
1024
|
+
while index < session.host.peers.len():
|
|
1025
|
+
let host_peer_ptr = session.host.peers.get(index) else:
|
|
1026
|
+
fatal(c"session host peer missing storage")
|
|
1027
|
+
|
|
1028
|
+
let same_address = unsafe: read(host_peer_ptr).address.equal(address)
|
|
1029
|
+
if same_address:
|
|
1030
|
+
if index < session.peers.len():
|
|
1031
|
+
let session_peer_ptr = session.peers.get(index) else:
|
|
1032
|
+
return Result[uint, net.Error].failure(error = net.Error(
|
|
1033
|
+
code = -6,
|
|
1034
|
+
message = string.String.from_str("session peer not found")
|
|
1035
|
+
))
|
|
1036
|
+
return Result[uint, net.Error].success(value = unsafe: read(session_peer_ptr).peer_id)
|
|
1037
|
+
return Result[uint, net.Error].failure(error = net.Error(
|
|
1038
|
+
code = -6,
|
|
1039
|
+
message = string.String.from_str("session peer not found")
|
|
1040
|
+
))
|
|
1041
|
+
index += 1
|
|
1042
|
+
|
|
1043
|
+
return Result[uint, net.Error].failure(error = net.Error(
|
|
1044
|
+
code = -6,
|
|
1045
|
+
message = string.String.from_str("session peer not found")
|
|
1046
|
+
))
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
async function drain_outgoing_session(session: ref[Session]) -> Result[bool, net.Error]:
|
|
1050
|
+
while true:
|
|
1051
|
+
let msg = session.outgoing.pop_front()
|
|
1052
|
+
match msg:
|
|
1053
|
+
Option.none:
|
|
1054
|
+
return Result[bool, net.Error].success(value = true)
|
|
1055
|
+
Option.some as payload:
|
|
1056
|
+
var outgoing = payload.value
|
|
1057
|
+
defer outgoing.release()
|
|
1058
|
+
if outgoing.reliable:
|
|
1059
|
+
let send_result = await session.host.send_reliable(
|
|
1060
|
+
outgoing.address,
|
|
1061
|
+
outgoing.payload.as_span(),
|
|
1062
|
+
0
|
|
1063
|
+
)
|
|
1064
|
+
match send_result:
|
|
1065
|
+
Result.failure:
|
|
1066
|
+
pass
|
|
1067
|
+
Result.success:
|
|
1068
|
+
pass
|
|
1069
|
+
else:
|
|
1070
|
+
let send_result = await session.host.send(
|
|
1071
|
+
outgoing.address,
|
|
1072
|
+
outgoing.payload.as_span()
|
|
1073
|
+
)
|
|
1074
|
+
match send_result:
|
|
1075
|
+
Result.failure:
|
|
1076
|
+
pass
|
|
1077
|
+
Result.success:
|
|
1078
|
+
pass
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
function drain_release_outgoing(session: ref[Session]) -> void:
|
|
1082
|
+
while true:
|
|
1083
|
+
let msg = session.outgoing.pop_front()
|
|
1084
|
+
match msg:
|
|
1085
|
+
Option.none:
|
|
1086
|
+
break
|
|
1087
|
+
Option.some as payload:
|
|
1088
|
+
var outgoing = payload.value
|
|
1089
|
+
outgoing.release()
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
function drain_release_outgoing_conn(conn: ref[Connection]) -> void:
|
|
1093
|
+
while true:
|
|
1094
|
+
let msg = conn.outgoing.pop_front()
|
|
1095
|
+
match msg:
|
|
1096
|
+
Option.none:
|
|
1097
|
+
break
|
|
1098
|
+
Option.some as payload:
|
|
1099
|
+
var outgoing = payload.value
|
|
1100
|
+
outgoing.release()
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
function drain_release_events(queue: ref[deque.Deque[PeerEvent]]) -> void:
|
|
1104
|
+
while true:
|
|
1105
|
+
let ev = queue.pop_front()
|
|
1106
|
+
match ev:
|
|
1107
|
+
Option.none:
|
|
1108
|
+
break
|
|
1109
|
+
Option.some as payload:
|
|
1110
|
+
var pe = payload.value
|
|
1111
|
+
if pe.kind == PeerEventKind.user_data:
|
|
1112
|
+
pe.payload.release()
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
function mark_peer_disconnected(session: ref[Session], peer_id: uint) -> void:
|
|
1116
|
+
var index: ptr_uint = 0
|
|
1117
|
+
while index < session.peers.len():
|
|
1118
|
+
let peer_ptr = session.peers.get(index) else:
|
|
1119
|
+
return
|
|
1120
|
+
if unsafe: read(peer_ptr).peer_id == peer_id:
|
|
1121
|
+
unsafe: read(peer_ptr).channel_state = ConnectionState.disconnected
|
|
1122
|
+
return
|
|
1123
|
+
index += 1
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
function connected_peer_count(session: ref[Session]) -> ptr_uint:
|
|
1127
|
+
var count: ptr_uint = 0
|
|
1128
|
+
var index: ptr_uint = 0
|
|
1129
|
+
while index < session.peers.len():
|
|
1130
|
+
let peer_ptr = session.peers.get(index) else:
|
|
1131
|
+
return count
|
|
1132
|
+
if unsafe: read(peer_ptr).channel_state == ConnectionState.connected:
|
|
1133
|
+
count += 1
|
|
1134
|
+
index += 1
|
|
1135
|
+
return count
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
function update_peer_last_recv(session: ref[Session], peer_id: uint) -> void:
|
|
1139
|
+
var index: ptr_uint = 0
|
|
1140
|
+
while index < session.peers.len():
|
|
1141
|
+
let peer_ptr = session.peers.get(index) else:
|
|
1142
|
+
return
|
|
1143
|
+
if unsafe: read(peer_ptr).peer_id == peer_id:
|
|
1144
|
+
unsafe: read(peer_ptr).frame_since_last_recv = 0
|
|
1145
|
+
return
|
|
1146
|
+
index += 1
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
function mark_peer_heartbeat_ack(session: ref[Session], peer_id: uint) -> void:
|
|
1150
|
+
var index: ptr_uint = 0
|
|
1151
|
+
while index < session.peers.len():
|
|
1152
|
+
let peer_ptr = session.peers.get(index) else:
|
|
1153
|
+
return
|
|
1154
|
+
if unsafe: read(peer_ptr).peer_id == peer_id:
|
|
1155
|
+
unsafe: read(peer_ptr).heartbeat_ack_received = true
|
|
1156
|
+
return
|
|
1157
|
+
index += 1
|