ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
discovery.cpp
Go to the documentation of this file.
1#include <emscripten/bind.h>
2#include "protocol_bytes.h"
3#include "byte_arrays.h"
4extern "C" {
7#include <ascii-chat/common.h>
8}
9#include <emscripten.h>
10#include <sodium.h>
11
12#include <cstddef>
13#include <cstdint>
14#include <string>
15#include <vector>
16
17EM_JS(void, discovery_send, (acds_protocol_t * p, int type, const void *data, size_t length),
18 { Module.discoverySend(p, type, HEAPU8.slice(data, data + length)); });
19static asciichat_error_t browser_send(void *p, packet_type_t type, const void *payload, size_t length) {
20 discovery_send(static_cast<acds_protocol_t *>(p), type, payload, length);
21 return ASCIICHAT_OK;
22}
23
24EMSCRIPTEN_DECLARE_VAL_TYPE(OptionalHandoffBytes);
25EMSCRIPTEN_DECLARE_VAL_TYPE(DiscoverySnapshotValue);
26EMSCRIPTEN_DECLARE_VAL_TYPE(HandoffActionValue);
27
28static std::vector<uint8_t> protocol_input(ProtocolBytes input, bool terminate = false) {
29 auto bytes = binding_copy_input(input);
30 if (terminate)
31 bytes.push_back(0);
32 return bytes;
33}
34
36public:
37 DiscoveryBinding() : session_(new acds_protocol_t{}) {
38 acds_protocol_init(session_, browser_send, session_);
39 }
41 if (session_)
42 acds_protocol_destroy(session_);
43 delete session_;
44 }
47 bool valid() const {
48 return session_ != nullptr;
49 }
50 uintptr_t callbackToken() const {
51 return reinterpret_cast<uintptr_t>(session_);
52 }
53 int enter(bool create, const std::string &name, ProtocolBytes password) {
54 auto secret = protocol_input(password, true);
55 int result = acds_protocol_enter(session_, create, name.c_str(), reinterpret_cast<const char *>(secret.data()));
56 sodium_memzero(secret.data(), secret.size());
57 return result;
58 }
59 int receive(unsigned type, ProtocolBytes packet) {
60 auto data = protocol_input(packet);
61 uint8_t empty = 0;
62 int result = acds_protocol_receive(session_, static_cast<packet_type_t>(type), data.empty() ? &empty : data.data(),
63 data.size());
64 if (!data.empty())
65 sodium_memzero(data.data(), data.size());
66 return result;
67 }
68 int leave() {
69 return acds_protocol_leave(session_);
70 }
71 DiscoverySnapshotValue snapshot() {
72 const auto &joined = session_->joined;
73 emscripten::val member = emscripten::val::object();
74 member.set("participantId", binding_copy_output(joined.participant_id, SESSION_ID_SIZE));
75 member.set("sessionId", binding_copy_output(joined.session_id, SESSION_ID_SIZE));
76 member.set("hostId",
77 binding_copy_output(joined.host_established ? joined.host_id : joined.initiator_id, SESSION_ID_SIZE));
78 member.set("turnUsername", std::string(joined.turn_username));
79 member.set("turnPassword", std::string(joined.turn_password));
80 member.set("serverAddress", std::string(joined.server_address));
81 member.set("serverPort", joined.server_port);
82 member.set("sessionType", joined.session_type);
83 member.set("quicPort", session_->quic_port);
84 member.set("webtransportUrl", std::string(session_->webtransport_url));
85 emscripten::val result = emscripten::val::object();
86 result.set("state", static_cast<int>(session_->state));
87 result.set("terminal", session_->terminal);
88 result.set("error", std::string(session_->error));
89 result.set("name", std::string(session_->name));
90 result.set("joined", member);
91 return DiscoverySnapshotValue(result);
92 }
93
94private:
95 acds_protocol_t *session_;
96};
97
98static OptionalHandoffBytes handoff_encode(int kind, ProtocolBytes session, ProtocolBytes participant,
99 unsigned generation, unsigned sequence, int capabilities, int nat_tier,
100 int port) {
101 auto session_id = protocol_input(session);
102 auto participant_id = protocol_input(participant);
103 if (session_id.size() != 16 || participant_id.size() != 16) {
104 SET_ERRNO(ERROR_INVALID_PARAM, "Handoff identifiers must be 16 bytes");
105 return OptionalHandoffBytes(emscripten::val::null());
106 }
107 uint8_t buffer[64];
109 message.kind = static_cast<uint8_t>(kind);
110 message.generation = generation;
111 message.sequence = sequence;
112 message.capabilities = static_cast<uint8_t>(capabilities);
113 message.nat_tier = static_cast<uint8_t>(nat_tier);
114 message.port = static_cast<uint16_t>(port);
115 memcpy(message.session, session_id.data(), SESSION_ID_SIZE);
116 memcpy(message.participant, participant_id.data(), SESSION_ID_SIZE);
117 consensus_handoff_encode(&message, buffer);
118 emscripten::val result = emscripten::val::global("Uint8Array").new_(sizeof(buffer));
119 result.call<void>("set", emscripten::val(emscripten::typed_memory_view(sizeof(buffer), buffer)));
120 return OptionalHandoffBytes(result);
121}
122static HandoffActionValue handoff_decode(ProtocolBytes packet) {
123 auto bytes = protocol_input(packet);
125 if (consensus_handoff_decode(bytes.data(), bytes.size(), &message) != ASCIICHAT_OK)
126 return HandoffActionValue(emscripten::val::null());
127 emscripten::val result = emscripten::val::object();
128 result.set("kind", message.kind);
129 result.set("participant", binding_copy_output(message.participant, SESSION_ID_SIZE));
130 result.set("generation", message.generation);
131 result.set("sequence", message.sequence);
132 return HandoffActionValue(result);
133}
134
135EMSCRIPTEN_BINDINGS(ascii_chat_discovery) {
136 emscripten::register_type<DiscoverySnapshotValue>(
137 "DiscoverySnapshotValue",
138 "{ state: number; terminal: boolean; error: string; name: string; joined: { participantId: Uint8Array; "
139 "sessionId: Uint8Array; hostId: Uint8Array; turnUsername: string; turnPassword: string; serverAddress: string; "
140 "serverPort: number; sessionType: number; quicPort: number; webtransportUrl: string } }");
141 emscripten::register_type<HandoffActionValue>(
142 "HandoffActionValue", "{ kind: number; participant: Uint8Array; generation: number; sequence: number } | null");
143 emscripten::register_type<ProtocolBytes>("ProtocolBytes", "Uint8Array");
144 emscripten::register_type<OptionalHandoffBytes>("OptionalHandoffBytes", "Uint8Array | null");
145 emscripten::class_<DiscoveryBinding>("DiscoveryBinding")
146 .constructor<>()
147 .function("valid", &DiscoveryBinding::valid)
148 .function("callbackToken", &DiscoveryBinding::callbackToken)
149 .function("enter", &DiscoveryBinding::enter)
150 .function("receive", &DiscoveryBinding::receive)
151 .function("leave", &DiscoveryBinding::leave)
152 .function("snapshot", &DiscoveryBinding::snapshot);
153 emscripten::function("handoffEncode", &handoff_encode);
154 emscripten::function("handoffDecode", &handoff_decode);
155}
void acds_protocol_init(acds_protocol_t *p, acds_protocol_send_fn send, void *user_data)
void acds_protocol_destroy(acds_protocol_t *p)
asciichat_error_t acds_protocol_receive(acds_protocol_t *p, packet_type_t type, const void *payload, size_t length)
asciichat_error_t acds_protocol_enter(acds_protocol_t *p, bool create, const char *name, const char *password)
asciichat_error_t acds_protocol_leave(acds_protocol_t *p)
struct acds_protocol acds_protocol_t
#define SET_ERRNO(code,...)
emscripten::val binding_copy_output(const void *data, size_t length)
Definition byte_arrays.h:18
std::vector< uint8_t > binding_copy_input(emscripten::val bytes)
Definition byte_arrays.h:6
DiscoveryBinding & operator=(const DiscoveryBinding &)=delete
DiscoveryBinding(const DiscoveryBinding &)=delete
uintptr_t callbackToken() const
Definition discovery.cpp:50
bool valid() const
Definition discovery.cpp:47
int enter(bool create, const std::string &name, ProtocolBytes password)
Definition discovery.cpp:53
int receive(unsigned type, ProtocolBytes packet)
Definition discovery.cpp:59
DiscoverySnapshotValue snapshot()
Definition discovery.cpp:71
⚙️ Common definitions, error codes, macros, and types shared throughout the application
EM_JS(void, discovery_send,(acds_protocol_t *p, int type, const void *data, size_t length), { Module.discoverySend(p, type, HEAPU8.slice(data, data+length));})
EMSCRIPTEN_BINDINGS(ascii_chat_discovery)
EMSCRIPTEN_DECLARE_VAL_TYPE(OptionalHandoffBytes)
unsigned short uint16_t
Definition common.h:61
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
#define SESSION_ID_SIZE
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:301
void consensus_handoff_encode(const consensus_handoff_message_t *m, uint8_t out[CONSENSUS_HANDOFF_HEADER])
Definition handoff.c:37
asciichat_error_t consensus_handoff_decode(const void *data, size_t length, consensus_handoff_message_t *m)
Definition handoff.c:16
uint8_t session_id[SESSION_ID_SIZE]
uint8_t participant_id[SESSION_ID_SIZE]
uint8_t session[SESSION_ID_SIZE]
Definition handoff.h:31
uint8_t participant[SESSION_ID_SIZE]
Definition handoff.h:32