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/clock.mt ADDED
@@ -0,0 +1,237 @@
1
+ import std.async as aio
2
+ import std.binary as bin
3
+ import std.bytes as bytes
4
+ import std.libuv as libuv
5
+ import std.net as net
6
+ import std.string as string
7
+
8
+ const clock_magic: array[ubyte, 8] = array[ubyte, 8](
9
+ 0x4Dub, 0x54ub, 0x43ub, 0x4Cub,
10
+ 0x4Fub, 0x43ub, 0x4Bub, 0x00ub
11
+ )
12
+
13
+ const sync_request: ubyte = 0x01
14
+ const sync_response: ubyte = 0x02
15
+
16
+ const header_bytes: ptr_uint = 9
17
+ const request_bytes: ptr_uint = 17
18
+ const response_bytes: ptr_uint = 25
19
+
20
+ const err_send_failed: int = -1
21
+ const err_recv_failed: int = -2
22
+ const err_timeout: int = -3
23
+ const err_bad_packet: int = -4
24
+
25
+ public struct ClockSync:
26
+ offset_us: int
27
+ rtt_us: ptr_uint
28
+
29
+ public struct TickClock:
30
+ tick: uint
31
+ rate: uint
32
+ epoch: ulong
33
+
34
+ public struct Error:
35
+ code: int
36
+ message: string.String
37
+
38
+
39
+ extending Error:
40
+ public editable function release() -> void:
41
+ this.message.release()
42
+
43
+
44
+ function clock_error(code: int, msg: str) -> Error:
45
+ return Error(code = code, message = string.String.from_str(msg))
46
+
47
+
48
+ public function monotonic_ns() -> ulong:
49
+ return libuv.hrtime()
50
+
51
+
52
+ public function monotonic_us() -> ulong:
53
+ return libuv.hrtime() / 1000ul
54
+
55
+
56
+ public function build_request(t1: ulong) -> bytes.Bytes:
57
+ var w = bin.Writer.with_capacity(request_bytes)
58
+ var i: ptr_uint = 0
59
+ while i < 8z:
60
+ w.write_ubyte(clock_magic[i])
61
+ i += 1
62
+ w.write_ubyte(sync_request)
63
+ w.write_ulong(t1)
64
+ return w.finish()
65
+
66
+
67
+ public function build_response(t1_client: ulong, t2_server: ulong) -> bytes.Bytes:
68
+ var w = bin.Writer.with_capacity(response_bytes)
69
+ var i: ptr_uint = 0
70
+ while i < 8z:
71
+ w.write_ubyte(clock_magic[i])
72
+ i += 1
73
+ w.write_ubyte(sync_response)
74
+ w.write_ulong(t1_client)
75
+ w.write_ulong(t2_server)
76
+ return w.finish()
77
+
78
+
79
+ function is_clock_packet(data: span[ubyte]) -> bool:
80
+ if data.len < header_bytes:
81
+ return false
82
+ var i: ptr_uint = 0
83
+ while i < 8z:
84
+ if data[i] != clock_magic[i]:
85
+ return false
86
+ i += 1
87
+ return true
88
+
89
+
90
+ public function parse_request(data: span[ubyte]) -> Result[ulong, Error]:
91
+ if data.len < request_bytes or data[8z] != sync_request:
92
+ return Result[ulong, Error].failure(error = clock_error(err_bad_packet, "not a clock sync request"))
93
+ var r = bin.reader(data)
94
+ var _bp = r.read_bytes(9z).map_error(proc(_: bin.Error) -> Error: clock_error(err_bad_packet, "malformed request"))?
95
+ _bp.release()
96
+ return r.read_ulong().map_error(proc(_: bin.Error) -> Error: clock_error(err_bad_packet, "malformed timestamp"))
97
+
98
+
99
+ public function parse_response(data: span[ubyte]) -> Result[ClockSync, Error]:
100
+ if data.len < response_bytes or data[8z] != sync_response:
101
+ return Result[ClockSync, Error].failure(error = clock_error(err_bad_packet, "not a clock sync response"))
102
+ var r = bin.reader(data)
103
+ match r.read_bytes(9z):
104
+ Result.failure:
105
+ return Result[ClockSync, Error].failure(error = clock_error(err_bad_packet, "malformed response"))
106
+ Result.success as bp:
107
+ bp.value.release()
108
+ match r.read_ulong():
109
+ Result.failure:
110
+ return Result[ClockSync, Error].failure(error = clock_error(err_bad_packet, "malformed t1"))
111
+ Result.success as t1_payload:
112
+ let t1_client = t1_payload.value
113
+ match r.read_ulong():
114
+ Result.failure:
115
+ return Result[ClockSync, Error].failure(error = clock_error(err_bad_packet, "malformed t2"))
116
+ Result.success as t2_payload:
117
+ let t2_svr = t2_payload.value
118
+ let t4 = libuv.hrtime()
119
+ let rtt = t4 - t1_client
120
+ let half_rtt = rtt / 2ul
121
+ if t2_svr > t1_client + half_rtt:
122
+ let offset_ns = t2_svr - t1_client - half_rtt
123
+ return Result[ClockSync, Error].success(
124
+ value = ClockSync(
125
+ offset_us = int<-(offset_ns / 1000ul),
126
+ rtt_us = ptr_uint<-(rtt / 1000ul)
127
+ )
128
+ )
129
+ return Result[ClockSync, Error].success(
130
+ value = ClockSync(offset_us = 0, rtt_us = ptr_uint<-(rtt / 1000ul))
131
+ )
132
+
133
+
134
+ public async function measure_offset(
135
+ socket: net.UdpSocket,
136
+ peer: net.SocketAddress
137
+ ) -> Result[ClockSync, Error]:
138
+ let t1 = libuv.hrtime()
139
+ var request = build_request(t1)
140
+ defer request.release()
141
+ (await socket.send_to(request.as_span(), peer)).map_error(proc(_: net.Error) -> Error:
142
+ clock_error(err_send_failed, "failed to send sync request")
143
+ )?
144
+
145
+ var frame: uint = 0
146
+ let recv_task = socket.recv_from(512)
147
+ while frame < 40:
148
+ if aio.completed(recv_task):
149
+ let recv_result = aio.result(recv_task)
150
+ match recv_result:
151
+ Result.failure:
152
+ return Result[ClockSync, Error].failure(
153
+ error = clock_error(err_recv_failed, "recv failed")
154
+ )
155
+ Result.success as dp:
156
+ var datagram = dp.value
157
+ defer datagram.data.release()
158
+ defer datagram.source.release()
159
+ if is_clock_packet(datagram.data.as_span()):
160
+ let parse_result = parse_response(datagram.data.as_span())
161
+ match parse_result:
162
+ Result.failure:
163
+ continue
164
+ Result.success as sp:
165
+ return Result[ClockSync, Error].success(value = sp.value)
166
+ await aio.sleep(50)
167
+ frame += 1
168
+
169
+ return Result[ClockSync, Error].failure(
170
+ error = clock_error(err_timeout, "clock sync request timed out")
171
+ )
172
+
173
+
174
+ public async function respond_to_sync(
175
+ socket: net.UdpSocket
176
+ ) -> void:
177
+ var recv_task = socket.recv_from(512)
178
+ var frame: uint = 0
179
+ while frame < 60:
180
+ if aio.completed(recv_task):
181
+ let recv_result = aio.result(recv_task)
182
+ match recv_result:
183
+ Result.success as dp:
184
+ var datagram = dp.value
185
+ defer datagram.data.release()
186
+ defer datagram.source.release()
187
+ if is_clock_packet(datagram.data.as_span()):
188
+ let parse_result = parse_request(datagram.data.as_span())
189
+ match parse_result:
190
+ Result.success as tp:
191
+ let t2 = libuv.hrtime()
192
+ var resp = build_response(tp.value, t2)
193
+ defer resp.release()
194
+ let _ = await socket.send_to(resp.as_span(), datagram.source)
195
+ Result.failure:
196
+ pass
197
+ Result.failure:
198
+ pass
199
+ recv_task = socket.recv_from(512)
200
+ await aio.sleep(100)
201
+ frame += 1
202
+
203
+
204
+ public function tick_clock_new(rate: uint) -> TickClock:
205
+ return TickClock(tick = 0u, rate = rate, epoch = monotonic_ns())
206
+
207
+
208
+ public function tick_clock_from_seed(rate: uint, seed_tick: uint, epoch_ns: ulong) -> TickClock:
209
+ return TickClock(tick = seed_tick, rate = rate, epoch = epoch_ns)
210
+
211
+
212
+ extending TickClock:
213
+ public editable function advance() -> void:
214
+ this.tick += 1
215
+
216
+
217
+ public function elapsed_ticks() -> uint:
218
+ let now = monotonic_ns()
219
+ let elapsed_ns = now - this.epoch
220
+ let tick_duration_ns = (1000000000ul / ulong<-this.rate)
221
+ if tick_duration_ns == 0ul:
222
+ return 0u
223
+ return uint<-(elapsed_ns / tick_duration_ns)
224
+
225
+
226
+ public function time_to_next_tick() -> ptr_uint:
227
+ let now = monotonic_ns()
228
+ let elapsed_ns = now - this.epoch
229
+ let tick_ns = (1000000000ul / (ulong<-this.rate))
230
+ let into_current = elapsed_ns % tick_ns
231
+ let remaining = tick_ns - into_current
232
+ return (remaining / 1000ul)
233
+
234
+
235
+ public function tick_ready() -> bool:
236
+ let elapsed = this.elapsed_ticks()
237
+ return elapsed > this.tick
@@ -0,0 +1,277 @@
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 discovery_magic: array[ubyte, 4] = array[ubyte, 4](
9
+ ubyte<-0x4D, ubyte<-0x54, ubyte<-0x44, ubyte<-0x53
10
+ )
11
+
12
+ const probe_bytes: ptr_uint = 4
13
+ const min_response_bytes: ptr_uint = 11
14
+ const default_discovery_port_offset: int = 1
15
+
16
+ public struct ServerInfo:
17
+ address: net.SocketAddress
18
+ game_port: int
19
+ player_count: ubyte
20
+ max_players: ubyte
21
+ game_name: string.String
22
+
23
+
24
+ extending ServerInfo:
25
+ public editable function release() -> void:
26
+ this.address.release()
27
+ this.game_name.release()
28
+
29
+
30
+ public function build_probe() -> bytes.Bytes:
31
+ var w = bin.Writer.with_capacity(probe_bytes)
32
+ w.write_ubyte(discovery_magic[0])
33
+ w.write_ubyte(discovery_magic[1])
34
+ w.write_ubyte(discovery_magic[2])
35
+ w.write_ubyte(discovery_magic[3])
36
+ return w.finish()
37
+
38
+
39
+ public function build_response(
40
+ game_port: int,
41
+ player_count: ubyte,
42
+ max_players: ubyte,
43
+ game_name: str
44
+ ) -> bytes.Bytes:
45
+ var w = bin.Writer.with_capacity(probe_bytes + 6z + game_name.len)
46
+ w.write_ubyte(discovery_magic[0])
47
+ w.write_ubyte(discovery_magic[1])
48
+ w.write_ubyte(discovery_magic[2])
49
+ w.write_ubyte(discovery_magic[3])
50
+ w.write_ushort(ushort<-game_port)
51
+ w.write_ubyte(player_count)
52
+ w.write_ubyte(max_players)
53
+ w.write_str(game_name)
54
+ return w.finish()
55
+
56
+
57
+ function is_probe(data: span[ubyte]) -> bool:
58
+ if data.len < probe_bytes:
59
+ return false
60
+ return (
61
+ data[0] == discovery_magic[0]
62
+ and data[1] == discovery_magic[1]
63
+ and data[2] == discovery_magic[2]
64
+ and data[3] == discovery_magic[3]
65
+ )
66
+
67
+
68
+ function discovery_error(message: str) -> net.Error:
69
+ return net.net_error(message)
70
+
71
+
72
+ function parse_response(data: span[ubyte]) -> Result[ServerInfo, net.Error]:
73
+ if data.len < min_response_bytes:
74
+ return Result[ServerInfo, net.Error].failure(
75
+ error = discovery_error("discovery response too short")
76
+ )
77
+
78
+ if (
79
+ data[0] != discovery_magic[0]
80
+ or data[1] != discovery_magic[1]
81
+ or data[2] != discovery_magic[2]
82
+ or data[3] != discovery_magic[3]
83
+ ):
84
+ return Result[ServerInfo, net.Error].failure(
85
+ error = discovery_error("discovery response bad magic")
86
+ )
87
+
88
+ var r = bin.reader(data)
89
+
90
+ var _bp = r.read_bytes(probe_bytes).map_error(proc(_: bin.Error) -> net.Error:
91
+ discovery_error("discovery response malformed header")
92
+ )?
93
+ _bp.release()
94
+
95
+ var game_port: int = 0
96
+ game_port = r.read_ushort().map_error(proc(_: bin.Error) -> net.Error:
97
+ discovery_error("discovery response malformed port")
98
+ )?
99
+
100
+ var player_count: ubyte = 0
101
+ player_count = r.read_ubyte().map_error(proc(_: bin.Error) -> net.Error:
102
+ discovery_error("discovery response malformed player_count")
103
+ )?
104
+
105
+ var max_players: ubyte = 0
106
+ max_players = r.read_ubyte().map_error(proc(_: bin.Error) -> net.Error:
107
+ discovery_error("discovery response malformed max_players")
108
+ )?
109
+
110
+ let game_name = r.read_str().map_error(proc(_: bin.Error) -> net.Error:
111
+ discovery_error("discovery response malformed name")
112
+ )?
113
+ return Result[ServerInfo, net.Error].success(value = ServerInfo(
114
+ address = zero[net.SocketAddress],
115
+ game_port = game_port,
116
+ player_count = player_count,
117
+ max_players = max_players,
118
+ game_name = game_name
119
+ ))
120
+
121
+
122
+ public async function announce(
123
+ game_port: int,
124
+ max_players: ubyte,
125
+ game_name: str
126
+ ) -> void:
127
+ let discovery_port = game_port + default_discovery_port_offset
128
+ match net.ipv4("0.0.0.0", discovery_port):
129
+ Result.failure:
130
+ return
131
+ Result.success as addr_p:
132
+ var announce_addr = addr_p.value
133
+ defer announce_addr.release()
134
+
135
+ match net.udp_bind(announce_addr):
136
+ Result.failure:
137
+ return
138
+ Result.success as bp:
139
+ var socket = bp.value
140
+ defer socket.release()
141
+
142
+ var response = build_response(game_port, ubyte<-0, max_players, game_name)
143
+ defer response.release()
144
+
145
+ while true:
146
+ let recv_task = socket.recv_from(1500)
147
+ var frame: uint = 0
148
+ while frame < 120:
149
+ if aio.completed(recv_task):
150
+ let recv_result = aio.result(recv_task)
151
+ match recv_result:
152
+ Result.success as dp:
153
+ var datagram = dp.value
154
+ if is_probe(datagram.data.as_span()):
155
+ let _ = await socket.send_to(
156
+ response.as_span(),
157
+ datagram.source
158
+ )
159
+ datagram.data.release()
160
+ datagram.source.release()
161
+ break
162
+ Result.failure:
163
+ break
164
+ await aio.sleep(100)
165
+ frame += 1
166
+
167
+
168
+ public async function discover_on(
169
+ runtime: aio.Runtime,
170
+ game_port: int,
171
+ timeout_frames: uint
172
+ ) -> Result[vec.Vec[ServerInfo], net.Error]:
173
+ match net.ipv4("0.0.0.0", 0):
174
+ Result.failure:
175
+ return Result[vec.Vec[ServerInfo], net.Error].failure(
176
+ error = discovery_error("failed to create local address")
177
+ )
178
+ Result.success as la:
179
+ var local_addr = la.value
180
+ defer local_addr.release()
181
+
182
+ match net.udp_bind_on(runtime, local_addr):
183
+ Result.failure:
184
+ return Result[vec.Vec[ServerInfo], net.Error].failure(
185
+ error = discovery_error("failed to bind discovery socket")
186
+ )
187
+ Result.success as bp:
188
+ var socket = bp.value
189
+ defer socket.release()
190
+
191
+ match socket.set_broadcast(true):
192
+ 0:
193
+ pass
194
+ _:
195
+ return Result[vec.Vec[ServerInfo], net.Error].failure(
196
+ error = discovery_error("failed to enable broadcast")
197
+ )
198
+
199
+ let broadcast_addr_result = net.ipv4(
200
+ "255.255.255.255",
201
+ game_port + default_discovery_port_offset
202
+ )
203
+ match broadcast_addr_result:
204
+ Result.failure:
205
+ return Result[vec.Vec[ServerInfo], net.Error].failure(
206
+ error = discovery_error("failed to create broadcast address")
207
+ )
208
+ Result.success as broad_addr:
209
+ var broadcast_addr = broad_addr.value
210
+ defer broadcast_addr.release()
211
+
212
+ var probe = build_probe()
213
+ defer probe.release()
214
+
215
+ var results = vec.Vec[ServerInfo].create()
216
+
217
+ var burst: uint = 0
218
+ while burst < 3:
219
+ let _ = await socket.send_to(probe.as_span(), broadcast_addr)
220
+ burst += 1
221
+
222
+ var recv_task = socket.recv_from(1500)
223
+ var frame: uint = 0
224
+ while frame < timeout_frames:
225
+ if aio.completed(recv_task):
226
+ let recv_result = aio.result(recv_task)
227
+ match recv_result:
228
+ Result.success as dp:
229
+ var datagram = dp.value
230
+ let parse_result = parse_response(
231
+ datagram.data.as_span()
232
+ )
233
+ datagram.data.release()
234
+ match parse_result:
235
+ Result.success as sp:
236
+ var info = sp.value
237
+ info.address = datagram.source
238
+ datagram.source = zero[net.SocketAddress]
239
+ if not already_discovered(
240
+ ref_of(results),
241
+ ref_of(info)
242
+ ):
243
+ results.push(info)
244
+ else:
245
+ info.release()
246
+ Result.failure:
247
+ datagram.source.release()
248
+ Result.failure:
249
+ break
250
+ recv_task = socket.recv_from(1500)
251
+ await aio.sleep(50)
252
+ frame += 1
253
+
254
+ return Result[vec.Vec[ServerInfo], net.Error].success(
255
+ value = results
256
+ )
257
+
258
+
259
+ public async function discover(
260
+ game_port: int,
261
+ timeout_frames: uint
262
+ ) -> Result[vec.Vec[ServerInfo], net.Error]:
263
+ return await discover_on(aio.current_runtime(), game_port, timeout_frames)
264
+
265
+
266
+ function already_discovered(
267
+ results: ref[vec.Vec[ServerInfo]],
268
+ info: ref[ServerInfo]
269
+ ) -> bool:
270
+ var index: ptr_uint = 0
271
+ while index < results.len():
272
+ let existing_ptr = results.get(index) else:
273
+ break
274
+ if unsafe: read(existing_ptr).address.equal(info.address):
275
+ return true
276
+ index += 1
277
+ return false