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/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)
|