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.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/docs/index.html +4 -3
  3. data/lib/milk_tea/base.rb +1 -1
  4. data/std/asset_pack.mt +267 -0
  5. data/std/async/libuv_runtime.mt +539 -0
  6. data/std/async/mailbox.mt +191 -0
  7. data/std/async/runtime.mt +87 -0
  8. data/std/async.mt +87 -0
  9. data/std/base64.mt +185 -0
  10. data/std/behavior_tree.mt +396 -0
  11. data/std/binary.mt +314 -0
  12. data/std/binary_heap.mt +157 -0
  13. data/std/bitset.mt +239 -0
  14. data/std/box2d.mt +535 -0
  15. data/std/bytes.mt +46 -0
  16. data/std/c/box2d.mt +1254 -0
  17. data/std/c/cgltf.mt +647 -0
  18. data/std/c/cjson.mt +106 -0
  19. data/std/c/crypto.mt +11 -0
  20. data/std/c/crypto_support.h +51 -0
  21. data/std/c/ctype.mt +19 -0
  22. data/std/c/ctype_bindgen.h +29 -0
  23. data/std/c/curl.mt +1223 -0
  24. data/std/c/enet.mt +473 -0
  25. data/std/c/errno.mt +41 -0
  26. data/std/c/errno_bindgen.h +53 -0
  27. data/std/c/flecs.mt +2945 -0
  28. data/std/c/fs.linux.mt +43 -0
  29. data/std/c/fs.windows.mt +43 -0
  30. data/std/c/fs_support.h +954 -0
  31. data/std/c/gl.mt +2065 -0
  32. data/std/c/gl_registry_helpers.h +10748 -0
  33. data/std/c/glfw.mt +499 -0
  34. data/std/c/libc.mt +167 -0
  35. data/std/c/libuv.mt +1491 -0
  36. data/std/c/math.mt +22 -0
  37. data/std/c/math_bindgen.h +31 -0
  38. data/std/c/miniaudio.mt +3420 -0
  39. data/std/c/pcre2.mt +735 -0
  40. data/std/c/process.mt +58 -0
  41. data/std/c/process_support.h +1147 -0
  42. data/std/c/raygui.mt +1602 -0
  43. data/std/c/raylib.mt +1245 -0
  44. data/std/c/raymath.mt +163 -0
  45. data/std/c/rlgl.mt +434 -0
  46. data/std/c/rpng.mt +60 -0
  47. data/std/c/rres.mt +187 -0
  48. data/std/c/sdl3.mt +4132 -0
  49. data/std/c/sqlite3.mt +951 -0
  50. data/std/c/stb_image.mt +52 -0
  51. data/std/c/stb_image_resize2.mt +128 -0
  52. data/std/c/stb_image_write.mt +19 -0
  53. data/std/c/stb_rect_pack.mt +38 -0
  54. data/std/c/stb_truetype.mt +175 -0
  55. data/std/c/stb_vorbis.mt +31 -0
  56. data/std/c/stdio.mt +90 -0
  57. data/std/c/steamworks.h +5769 -0
  58. data/std/c/steamworks.mt +4771 -0
  59. data/std/c/string.mt +16 -0
  60. data/std/c/string_bindgen.h +36 -0
  61. data/std/c/sync.mt +33 -0
  62. data/std/c/sync_support.h +219 -0
  63. data/std/c/terminal.mt +24 -0
  64. data/std/c/terminal_support.h +408 -0
  65. data/std/c/time.mt +26 -0
  66. data/std/c/tls.mt +24 -0
  67. data/std/c/tls_support.h +626 -0
  68. data/std/c/tracy.mt +53 -0
  69. data/std/c/zlib.mt +20 -0
  70. data/std/c/zlib_support.h +239 -0
  71. data/std/c/zstd.mt +231 -0
  72. data/std/cell.mt +60 -0
  73. data/std/cgltf.mt +115 -0
  74. data/std/cjson.mt +90 -0
  75. data/std/cli.mt +768 -0
  76. data/std/cookie.mt +198 -0
  77. data/std/counter.mt +160 -0
  78. data/std/crypto.mt +74 -0
  79. data/std/cstring.mt +13 -0
  80. data/std/ctype.mt +52 -0
  81. data/std/curl/runtime.mt +261 -0
  82. data/std/curl.mt +38 -0
  83. data/std/deque.mt +348 -0
  84. data/std/encoding.mt +93 -0
  85. data/std/enet.mt +133 -0
  86. data/std/env.mt +41 -0
  87. data/std/errno.mt +47 -0
  88. data/std/flecs.mt +978 -0
  89. data/std/fmt.mt +271 -0
  90. data/std/fs.linux.mt +696 -0
  91. data/std/fs.windows.mt +711 -0
  92. data/std/fsm.mt +267 -0
  93. data/std/gl.mt +2062 -0
  94. data/std/glfw.mt +471 -0
  95. data/std/goap.mt +342 -0
  96. data/std/graph.mt +521 -0
  97. data/std/gzip.mt +77 -0
  98. data/std/hash.mt +429 -0
  99. data/std/http/server.mt +810 -0
  100. data/std/http.mt +1227 -0
  101. data/std/intern.mt +53 -0
  102. data/std/jobs.mt +288 -0
  103. data/std/json.mt +627 -0
  104. data/std/libc.mt +34 -0
  105. data/std/libuv.mt +554 -0
  106. data/std/linear_algebra.mt +243 -0
  107. data/std/linked_map.mt +372 -0
  108. data/std/linked_map_view.mt +49 -0
  109. data/std/linked_set.mt +117 -0
  110. data/std/log.mt +69 -0
  111. data/std/map.mt +418 -0
  112. data/std/math.mt +24 -0
  113. data/std/mem/arena.mt +137 -0
  114. data/std/mem/endian.mt +51 -0
  115. data/std/mem/heap.mt +298 -0
  116. data/std/mem/pool.mt +164 -0
  117. data/std/mem/stack.mt +47 -0
  118. data/std/mem/tracking.mt +119 -0
  119. data/std/miniaudio.mt +1299 -0
  120. data/std/multiset.mt +191 -0
  121. data/std/net/channel.mt +727 -0
  122. data/std/net/clock.mt +237 -0
  123. data/std/net/discovery.mt +277 -0
  124. data/std/net/lobby.mt +813 -0
  125. data/std/net/manager.mt +539 -0
  126. data/std/net/mux.mt +839 -0
  127. data/std/net/nat.mt +122 -0
  128. data/std/net/packet.mt +219 -0
  129. data/std/net/punch.mt +162 -0
  130. data/std/net/rpc.mt +119 -0
  131. data/std/net/session.mt +1157 -0
  132. data/std/net/stun.mt +252 -0
  133. data/std/net/sync.mt +183 -0
  134. data/std/net/turn.mt +459 -0
  135. data/std/net.mt +2944 -0
  136. data/std/oauth2.mt +408 -0
  137. data/std/option.mt +51 -0
  138. data/std/ordered_map.mt +497 -0
  139. data/std/ordered_set.mt +348 -0
  140. data/std/path.mt +368 -0
  141. data/std/pcre2/runtime.mt +35 -0
  142. data/std/pcre2.mt +78 -0
  143. data/std/priority_queue.mt +55 -0
  144. data/std/process.mt +837 -0
  145. data/std/queue.mt +55 -0
  146. data/std/random.mt +146 -0
  147. data/std/raygui.mt +99 -0
  148. data/std/raylib/debug_console.mt +71 -0
  149. data/std/raylib/easing.mt +388 -0
  150. data/std/raylib/packed_assets.mt +255 -0
  151. data/std/raylib/runtime.mt +30 -0
  152. data/std/raylib/tracy_gpu.mt +37 -0
  153. data/std/raylib.mt +1510 -0
  154. data/std/raymath.mt +153 -0
  155. data/std/result.mt +89 -0
  156. data/std/rlgl.mt +268 -0
  157. data/std/rpng.mt +46 -0
  158. data/std/rres.mt +36 -0
  159. data/std/sdl3/runtime.mt +54 -0
  160. data/std/sdl3.mt +1731 -0
  161. data/std/serialize.mt +68 -0
  162. data/std/set.mt +124 -0
  163. data/std/spatial.mt +176 -0
  164. data/std/sqlite3.mt +151 -0
  165. data/std/stack.mt +55 -0
  166. data/std/stb_image.mt +47 -0
  167. data/std/stb_image_resize2.mt +41 -0
  168. data/std/stb_image_write.mt +17 -0
  169. data/std/stb_rect_pack.mt +15 -0
  170. data/std/stb_truetype.mt +77 -0
  171. data/std/stb_vorbis.mt +16 -0
  172. data/std/stdio.mt +88 -0
  173. data/std/steamworks.mt +1542 -0
  174. data/std/str.mt +293 -0
  175. data/std/string.mt +234 -0
  176. data/std/sync.mt +194 -0
  177. data/std/tar.mt +704 -0
  178. data/std/terminal.mt +1002 -0
  179. data/std/testing.mt +266 -0
  180. data/std/thread.mt +120 -0
  181. data/std/time.mt +105 -0
  182. data/std/tls.mt +616 -0
  183. data/std/toml.mt +1310 -0
  184. data/std/tracy.mt +42 -0
  185. data/std/uri.mt +118 -0
  186. data/std/url.mt +372 -0
  187. data/std/vec.mt +433 -0
  188. data/std/zstd.mt +94 -0
  189. metadata +187 -2
data/std/net/nat.mt ADDED
@@ -0,0 +1,122 @@
1
+ import std.net as net
2
+ import std.net.stun as stun
3
+ import std.string as string
4
+
5
+ const google_primary: str = "stun.l.google.com"
6
+ const google_alternate: str = "stun1.l.google.com"
7
+ const google_port: int = 19302
8
+
9
+ const err_resolve: int = -1
10
+ const err_bind: int = -2
11
+ const err_discover: int = -3
12
+ const err_blocked: int = -4
13
+
14
+ public enum NatType: ubyte
15
+ blocked = 0
16
+ open_internet = 1
17
+ cone = 2
18
+ symmetric = 3
19
+
20
+ public struct NatResult:
21
+ nat_type: NatType
22
+ public_address: net.SocketAddress
23
+
24
+ public struct Error:
25
+ code: int
26
+ message: string.String
27
+
28
+
29
+ extending Error:
30
+ public editable function release() -> void:
31
+ this.message.release()
32
+
33
+
34
+ extending NatResult:
35
+ public editable function release() -> void:
36
+ this.public_address.release()
37
+
38
+
39
+ function nat_error(code: int, msg: str) -> Error:
40
+ return Error(code = code, message = string.String.from_str(msg))
41
+
42
+
43
+ public async function detect(
44
+ socket: net.UdpSocket,
45
+ primary_server: net.SocketAddress,
46
+ alternate_server: net.SocketAddress
47
+ ) -> Result[NatResult, Error]:
48
+ let primary_result = await stun.resolve_public_address(socket, primary_server)
49
+ match primary_result:
50
+ Result.failure:
51
+ return Result[NatResult, Error].failure(
52
+ error = nat_error(err_blocked, "UDP blocked or STUN unreachable")
53
+ )
54
+ Result.success as pr:
55
+ var public_1 = pr.value
56
+ defer public_1.release()
57
+
58
+ let local_result = socket.local_address()
59
+ match local_result:
60
+ Result.failure:
61
+ return Result[NatResult, Error].failure(
62
+ error = nat_error(err_bind, "failed to get local address")
63
+ )
64
+ Result.success as lp:
65
+ if public_1.public_address.equal(lp.value):
66
+ return Result[NatResult, Error].success(
67
+ value = NatResult(
68
+ nat_type = NatType.open_internet,
69
+ public_address = public_1.public_address
70
+ )
71
+ )
72
+
73
+ let alt_result_output = await stun.resolve_public_address(socket, alternate_server)
74
+ match alt_result_output:
75
+ Result.failure:
76
+ return Result[NatResult, Error].failure(
77
+ error = nat_error(err_discover, "alternate STUN probe failed")
78
+ )
79
+ Result.success as ar:
80
+ var public_2 = ar.value
81
+ defer public_2.release()
82
+
83
+ if not public_1.public_address.equal(public_2.public_address):
84
+ return Result[NatResult, Error].success(
85
+ value = NatResult(
86
+ nat_type = NatType.symmetric,
87
+ public_address = public_1.public_address
88
+ )
89
+ )
90
+
91
+ return Result[NatResult, Error].success(
92
+ value = NatResult(
93
+ nat_type = NatType.cone,
94
+ public_address = public_1.public_address
95
+ )
96
+ )
97
+
98
+
99
+ public async function detect_default(
100
+ socket: net.UdpSocket
101
+ ) -> Result[NatResult, Error]:
102
+ let primary_result = net.ipv4(google_primary, google_port)
103
+ match primary_result:
104
+ Result.failure:
105
+ return Result[NatResult, Error].failure(
106
+ error = nat_error(err_resolve, "failed to resolve primary STUN server")
107
+ )
108
+ Result.success as pp:
109
+ var primary = pp.value
110
+ defer primary.release()
111
+
112
+ let alt_result = net.ipv4(google_alternate, google_port)
113
+ match alt_result:
114
+ Result.failure:
115
+ return Result[NatResult, Error].failure(
116
+ error = nat_error(err_resolve, "failed to resolve alternate STUN server")
117
+ )
118
+ Result.success as ap:
119
+ var alternate = ap.value
120
+ defer alternate.release()
121
+
122
+ return await detect(socket, primary, alternate)
data/std/net/packet.mt ADDED
@@ -0,0 +1,219 @@
1
+ import std.async as aio
2
+ import std.bytes as bytes
3
+ import std.net as net
4
+ import std.string as string
5
+ import std.vec as vec
6
+
7
+ const frame_header_bytes: ptr_uint = 4
8
+ const discard_chunk_bytes: ptr_uint = 4096
9
+ const max_frame_payload_bytes: ptr_uint = 0xFFFFFFFF
10
+
11
+ public struct Error:
12
+ code: int
13
+ message: string.String
14
+
15
+ public struct Stream:
16
+ stream: net.TcpStream
17
+ max_packet_bytes: ptr_uint
18
+
19
+ public struct Listener:
20
+ listener: net.TcpListener
21
+ max_packet_bytes: ptr_uint
22
+
23
+
24
+ function packet_error(code: int, message: str) -> Error:
25
+ return Error(code = code, message = string.String.from_str(message))
26
+
27
+
28
+ function error_from_net(source: net.Error) -> Error:
29
+ return Error(code = source.code, message = source.message)
30
+
31
+
32
+ function encode_packet_length(length: ptr_uint) -> array[ubyte, 4]:
33
+ let raw = uint<-length
34
+ return array[ubyte, 4](
35
+ ubyte<-((raw >> 24) & 255),
36
+ ubyte<-((raw >> 16) & 255),
37
+ ubyte<-((raw >> 8) & 255),
38
+ ubyte<-(raw & 255)
39
+ )
40
+
41
+
42
+ function decode_packet_length(header: bytes.Bytes) -> ptr_uint:
43
+ if header.len != frame_header_bytes:
44
+ fatal(c"packet frame header length mismatch")
45
+
46
+ let header_span = header.as_span()
47
+ let length = ((uint<-header_span[0]) << 24) | ((uint<-header_span[1]) << 16) | ((uint<-header_span[2]) << 8) | (uint<-header_span[3])
48
+ return length
49
+
50
+
51
+ function frame_bytes(content: span[ubyte]) -> bytes.Bytes:
52
+ var framed = vec.Vec[ubyte].with_capacity(frame_header_bytes + content.len)
53
+ defer framed.release()
54
+
55
+ let header = encode_packet_length(content.len)
56
+ framed.append_array(header)
57
+ framed.append_span(content)
58
+ return bytes.Bytes.copy(framed.as_span())
59
+
60
+
61
+ async function discard_exactly(stream: net.TcpStream, byte_count: ptr_uint) -> Result[bool, Error]:
62
+ var remaining = byte_count
63
+ while remaining != 0:
64
+ var chunk_bytes = discard_chunk_bytes
65
+ if remaining < chunk_bytes:
66
+ chunk_bytes = remaining
67
+
68
+ let discard_result = await stream.read_exactly(chunk_bytes)
69
+ match discard_result:
70
+ Result.failure as payload:
71
+ return Result[bool, Error].failure(error = error_from_net(payload.error))
72
+ Result.success as payload:
73
+ var chunk = payload.value
74
+ chunk.release()
75
+ remaining -= chunk_bytes
76
+
77
+ return Result[bool, Error].success(value = true)
78
+
79
+
80
+ public async function connect_on(
81
+ runtime: aio.Runtime,
82
+ address: net.SocketAddress,
83
+ max_packet_bytes: ptr_uint
84
+ ) -> Result[Stream, Error]:
85
+ let connect_result = await net.connect_on(runtime, address)
86
+ match connect_result:
87
+ Result.failure as payload:
88
+ return Result[Stream, Error].failure(error = error_from_net(payload.error))
89
+ Result.success as payload:
90
+ return Result[Stream, Error].success(value = wrap(payload.value, max_packet_bytes))
91
+
92
+
93
+ public async function connect(address: net.SocketAddress, max_packet_bytes: ptr_uint) -> Result[Stream, Error]:
94
+ return await connect_on(aio.current_runtime(), address, max_packet_bytes)
95
+
96
+
97
+ public function listen_on(
98
+ runtime: aio.Runtime,
99
+ address: net.SocketAddress,
100
+ backlog: int,
101
+ max_packet_bytes: ptr_uint
102
+ ) -> Result[Listener, Error]:
103
+ let listen_result = net.listen_on(runtime, address, backlog)
104
+ match listen_result:
105
+ Result.failure as payload:
106
+ return Result[Listener, Error].failure(error = error_from_net(payload.error))
107
+ Result.success as payload:
108
+ return Result[Listener, Error].success(value = Listener(
109
+ listener = payload.value,
110
+ max_packet_bytes = max_packet_bytes
111
+ ))
112
+
113
+
114
+ public function listen(address: net.SocketAddress, backlog: int, max_packet_bytes: ptr_uint) -> Result[Listener, Error]:
115
+ return listen_on(aio.current_runtime(), address, backlog, max_packet_bytes)
116
+
117
+
118
+ public function wrap(stream: net.TcpStream, max_packet_bytes: ptr_uint) -> Stream:
119
+ return Stream(stream = stream, max_packet_bytes = max_packet_bytes)
120
+
121
+
122
+ extending Error:
123
+ public editable function release() -> void:
124
+ this.message.release()
125
+
126
+
127
+ extending Stream:
128
+ public editable function release() -> void:
129
+ this.stream.release()
130
+
131
+
132
+ public function local_address() -> Result[net.SocketAddress, net.Error]:
133
+ return this.stream.local_address()
134
+
135
+
136
+ public function peer_address() -> Result[net.SocketAddress, net.Error]:
137
+ return this.stream.peer_address()
138
+
139
+
140
+ public function socket_fd() -> Result[int, net.Error]:
141
+ return this.stream.socket_fd()
142
+
143
+
144
+ public function max_packet_bytes() -> ptr_uint:
145
+ return this.max_packet_bytes
146
+
147
+
148
+ public function shutdown() -> Task[Result[bool, net.Error]]:
149
+ return this.stream.shutdown()
150
+
151
+
152
+ public async function write_packet(content: span[ubyte]) -> Result[ptr_uint, Error]:
153
+ if content.len > this.max_packet_bytes:
154
+ return Result[ptr_uint, Error].failure(error = packet_error(-2, "packet exceeds configured maximum"))
155
+
156
+ if content.len > max_frame_payload_bytes:
157
+ return Result[ptr_uint, Error].failure(error = packet_error(-3, "packet exceeds 32-bit frame limit"))
158
+
159
+ var framed = frame_bytes(content)
160
+ defer framed.release()
161
+
162
+ let write_result = await this.stream.write_bytes(framed.as_span())
163
+ match write_result:
164
+ Result.failure as error_payload:
165
+ return Result[ptr_uint, Error].failure(error = error_from_net(error_payload.error))
166
+ Result.success as write_payload:
167
+ unsafe: write_payload.value
168
+ return Result[ptr_uint, Error].success(value = content.len)
169
+
170
+
171
+ public async function read_packet() -> Result[bytes.Bytes, Error]:
172
+ let header_result = await this.stream.read_exactly(frame_header_bytes)
173
+ match header_result:
174
+ Result.failure as payload:
175
+ return Result[bytes.Bytes, Error].failure(error = error_from_net(payload.error))
176
+ Result.success as payload:
177
+ var header = payload.value
178
+ defer header.release()
179
+ let packet_length = decode_packet_length(header)
180
+ if packet_length > this.max_packet_bytes:
181
+ let discard_result = await discard_exactly(this.stream, packet_length)
182
+ match discard_result:
183
+ Result.failure as discard_payload:
184
+ return Result[bytes.Bytes, Error].failure(error = discard_payload.error)
185
+ Result.success as discard_payload:
186
+ unsafe: discard_payload.value
187
+ return Result[bytes.Bytes, Error].failure(error = packet_error(
188
+ -2,
189
+ "packet exceeds configured maximum"
190
+ ))
191
+
192
+ let payload_result = await this.stream.read_exactly(packet_length)
193
+ match payload_result:
194
+ Result.failure as read_payload:
195
+ return Result[bytes.Bytes, Error].failure(error = error_from_net(read_payload.error))
196
+ Result.success as read_payload:
197
+ return Result[bytes.Bytes, Error].success(value = read_payload.value)
198
+
199
+
200
+ extending Listener:
201
+ public editable function release() -> void:
202
+ this.listener.release()
203
+
204
+
205
+ public function local_address() -> Result[net.SocketAddress, net.Error]:
206
+ return this.listener.local_address()
207
+
208
+
209
+ public function max_packet_bytes() -> ptr_uint:
210
+ return this.max_packet_bytes
211
+
212
+
213
+ public async function accept() -> Result[Stream, Error]:
214
+ let accept_result = await this.listener.accept()
215
+ match accept_result:
216
+ Result.failure as payload:
217
+ return Result[Stream, Error].failure(error = error_from_net(payload.error))
218
+ Result.success as payload:
219
+ return Result[Stream, Error].success(value = wrap(payload.value, this.max_packet_bytes))
data/std/net/punch.mt ADDED
@@ -0,0 +1,162 @@
1
+ import std.async as aio
2
+ import std.binary as bin
3
+ import std.bytes as bytes
4
+ import std.net as net
5
+ import std.string as string
6
+ import std.vec as vec
7
+
8
+ const punch_magic: array[ubyte, 4] = array[ubyte, 4](0x4D, 0x54, 0x50, 0x43)
9
+
10
+ const burst_count: uint = 3
11
+ const burst_delay: ptr_uint = 20
12
+ const timeout_frames: uint = 120
13
+
14
+ const err_send_failed: int = -1
15
+ const err_no_response: int = -2
16
+
17
+ public enum CandidateKind: ubyte
18
+ local = 0
19
+ server_reflexive = 1
20
+
21
+ public struct Candidate:
22
+ address: net.SocketAddress
23
+ kind: CandidateKind
24
+
25
+ public struct PunchResult:
26
+ address: net.SocketAddress
27
+
28
+ public struct Error:
29
+ code: int
30
+ message: string.String
31
+
32
+
33
+ extending Error:
34
+ public editable function release() -> void:
35
+ this.message.release()
36
+
37
+
38
+ extending Candidate:
39
+ public editable function release() -> void:
40
+ this.address.release()
41
+
42
+
43
+ extending PunchResult:
44
+ public editable function release() -> void:
45
+ this.address.release()
46
+
47
+
48
+ function punch_error(code: int, msg: str) -> Error:
49
+ return Error(code = code, message = string.String.from_str(msg))
50
+
51
+
52
+ function matches_any_candidate(source: net.SocketAddress, candidates: vec.Vec[Candidate]) -> bool:
53
+ var i: ptr_uint = 0
54
+ while i < candidates.len:
55
+ let cand_ptr = candidates.get(i) else:
56
+ break
57
+ if source.equal(unsafe: read(cand_ptr).address):
58
+ return true
59
+ i += 1
60
+ return false
61
+
62
+
63
+ public function build_punch_probe() -> bytes.Bytes:
64
+ var w = bin.Writer.with_capacity(4)
65
+ w.write_ubyte(punch_magic[0])
66
+ w.write_ubyte(punch_magic[1])
67
+ w.write_ubyte(punch_magic[2])
68
+ w.write_ubyte(punch_magic[3])
69
+ return w.finish()
70
+
71
+
72
+ public function is_punch_probe(data: span[ubyte]) -> bool:
73
+ if data.len < 4z:
74
+ return false
75
+ return data[0] == punch_magic[0] and data[1] == punch_magic[1] and data[2] == punch_magic[2] and data[3] == punch_magic[3]
76
+
77
+
78
+ function result_from_datagram(
79
+ datagram: net.UdpDatagram,
80
+ remote_candidates: vec.Vec[Candidate]
81
+ ) -> Option[PunchResult]:
82
+ if not is_punch_probe(datagram.data.as_span()):
83
+ return Option[PunchResult].none()
84
+ if not matches_any_candidate(datagram.source, remote_candidates):
85
+ return Option[PunchResult].none()
86
+ let addr_copy = datagram.source.copy()
87
+ match addr_copy:
88
+ Result.success as ac:
89
+ return Option[PunchResult].some(value = PunchResult(address = ac.value))
90
+ Result.failure:
91
+ pass
92
+ return Option[PunchResult].none()
93
+
94
+
95
+ function poll_recv(
96
+ recv_task: Task[Result[net.UdpDatagram, net.Error]],
97
+ remote_candidates: vec.Vec[Candidate]
98
+ ) -> Option[PunchResult]:
99
+ if not aio.completed(recv_task):
100
+ return Option[PunchResult].none()
101
+ let recv_result = aio.result(recv_task)
102
+ match recv_result:
103
+ Result.failure:
104
+ return Option[PunchResult].none()
105
+ Result.success as dp:
106
+ var dg = dp.value
107
+ defer dg.data.release()
108
+ defer dg.source.release()
109
+ return result_from_datagram(dg, remote_candidates)
110
+
111
+
112
+ public async function punch(
113
+ socket: net.UdpSocket,
114
+ remote_candidates: vec.Vec[Candidate]
115
+ ) -> Result[PunchResult, Error]:
116
+ var probe = build_punch_probe()
117
+ defer probe.release()
118
+ var recv_task = socket.recv_from(512)
119
+
120
+ var c: uint = 0
121
+ while c < burst_count:
122
+ var i: ptr_uint = 0
123
+ while i < remote_candidates.len:
124
+ let cand_ptr = remote_candidates.get(i) else:
125
+ break
126
+ let send_result = await socket.send_to(probe.as_span(), unsafe: read(cand_ptr).address)
127
+ match send_result:
128
+ Result.failure as sp:
129
+ return Result[PunchResult, Error].failure(
130
+ error = punch_error(err_send_failed, "burst send failed")
131
+ )
132
+ Result.success:
133
+ pass
134
+ i += 1
135
+
136
+ let opt = poll_recv(recv_task, remote_candidates)
137
+ match opt:
138
+ Option.some as rp:
139
+ return Result[PunchResult, Error].success(value = rp.value)
140
+ Option.none:
141
+ pass
142
+ recv_task = socket.recv_from(512)
143
+
144
+ if c < burst_count - 1:
145
+ await aio.sleep(burst_delay)
146
+ c += 1
147
+
148
+ var frame: uint = 0
149
+ while frame < timeout_frames:
150
+ let opt = poll_recv(recv_task, remote_candidates)
151
+ match opt:
152
+ Option.some as rp:
153
+ return Result[PunchResult, Error].success(value = rp.value)
154
+ Option.none:
155
+ pass
156
+ recv_task = socket.recv_from(512)
157
+ await aio.sleep(50)
158
+ frame += 1
159
+
160
+ return Result[PunchResult, Error].failure(
161
+ error = punch_error(err_no_response, "no punch response received")
162
+ )
data/std/net/rpc.mt ADDED
@@ -0,0 +1,119 @@
1
+ import std.async as aio
2
+ import std.binary as bin
3
+ import std.bytes as bytes
4
+ import std.net.mux as mux
5
+ import std.string as string
6
+
7
+ const header_bytes: ptr_uint = 4
8
+
9
+ const err_send_failed: int = -1
10
+ const err_timeout: int = -2
11
+ const err_unexpected: int = -3
12
+
13
+ public struct Error:
14
+ code: int
15
+ message: string.String
16
+
17
+
18
+ extending Error:
19
+ public editable function release() -> void:
20
+ this.message.release()
21
+
22
+
23
+ function rpc_error(code: int, msg: str) -> Error:
24
+ return Error(code = code, message = string.String.from_str(msg))
25
+
26
+
27
+ public function build_call(request_id: uint, payload: span[ubyte]) -> bytes.Bytes:
28
+ var w = bin.Writer.with_capacity(header_bytes + payload.len)
29
+ w.write_uint(request_id)
30
+ var i: ptr_uint = 0
31
+ while i < payload.len:
32
+ w.write_ubyte(payload[i])
33
+ i += 1
34
+ return w.finish()
35
+
36
+
37
+ public function build_reply(request_id: uint, payload: span[ubyte]) -> bytes.Bytes:
38
+ return build_call(request_id, payload)
39
+
40
+
41
+ public function parse_request_id(data: span[ubyte]) -> Result[uint, Error]:
42
+ if data.len < header_bytes:
43
+ return Result[uint, Error].failure(error = rpc_error(err_unexpected, "rpc frame too small"))
44
+ var r = bin.reader(data)
45
+ return r.read_uint().map_error(proc(_: bin.Error) -> Error: rpc_error(err_unexpected, "malformed rpc frame"))
46
+
47
+
48
+ public function payload_after_header(data: span[ubyte]) -> span[ubyte]:
49
+ if data.len <= header_bytes:
50
+ let empty = bytes.Bytes.empty()
51
+ let result = empty.as_span()
52
+ return result
53
+ unsafe:
54
+ return span[ubyte](data = data.data + header_bytes, len = data.len - header_bytes)
55
+
56
+
57
+ public async function call_and_wait(
58
+ conn: ref[mux.MuxedConnection],
59
+ channel_id: ubyte,
60
+ call_type_id: ushort,
61
+ response_type_id: ushort,
62
+ request_id: uint,
63
+ payload: span[ubyte]
64
+ ) -> Result[bytes.Bytes, Error]:
65
+ var call_packet = build_call(request_id, payload)
66
+ defer call_packet.release()
67
+
68
+ let send_result = await conn.mux_send(channel_id, call_type_id, call_packet.as_span(), mux.flag_reliable)
69
+ match send_result:
70
+ Result.failure:
71
+ return Result[bytes.Bytes, Error].failure(error = rpc_error(err_send_failed, "rpc send failed"))
72
+ Result.success:
73
+ pass
74
+
75
+ var frame: uint = 0
76
+ while frame < 120:
77
+ let msg_opt = conn.try_recv()
78
+ match msg_opt:
79
+ Option.some as mp:
80
+ var msg = mp.value
81
+ defer msg.release()
82
+ if msg.type_id == response_type_id and msg.channel_id == channel_id:
83
+ let id_result = parse_request_id(msg.payload.as_span())
84
+ match id_result:
85
+ Result.success as ip:
86
+ if ip.value == request_id:
87
+ let result_data = payload_after_header(msg.payload.as_span())
88
+ var copy = bin.Writer.with_capacity(result_data.len)
89
+ var i: ptr_uint = 0
90
+ while i < result_data.len:
91
+ copy.write_ubyte(result_data[i])
92
+ i += 1
93
+ return Result[bytes.Bytes, Error].success(value = copy.finish())
94
+ Result.failure:
95
+ pass
96
+ Option.none:
97
+ pass
98
+
99
+ await aio.sleep(16)
100
+ frame += 1
101
+
102
+ return Result[bytes.Bytes, Error].failure(error = rpc_error(err_timeout, "rpc call timed out"))
103
+
104
+
105
+ public async function send_reply(
106
+ conn: ref[mux.MuxedConnection],
107
+ channel_id: ubyte,
108
+ response_type_id: ushort,
109
+ request_id: uint,
110
+ payload: span[ubyte]
111
+ ) -> Result[bool, Error]:
112
+ var reply_packet = build_reply(request_id, payload)
113
+ defer reply_packet.release()
114
+ let send_result = await conn.mux_send(channel_id, response_type_id, reply_packet.as_span(), mux.flag_reliable)
115
+ match send_result:
116
+ Result.failure:
117
+ return Result[bool, Error].failure(error = rpc_error(err_send_failed, "reply send failed"))
118
+ Result.success:
119
+ return Result[bool, Error].success(value = true)