Skip to content

Commit 8f501e4

Browse files
SyntaxNyahclaude
andcommitted
Add native DRO (Danganronpa Online) server support
Allows AO2-Client to connect directly to DRO servers without a proxy: - Detect DRO handshake (DRO#version#%) and set m_dro_mode flag - Handle CASEA chunked character-list delivery, accumulate into m_casea_buffer and emit a synthetic SC packet on the final empty chunk - Trim incoming MS packets to 15 fields in DRO mode so the courtroom renderer doesn't receive unexpected extra fields Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c73dbaf commit 8f501e4

2 files changed

Lines changed: 47 additions & 2 deletions

File tree

src/aoapplication.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ class AOApplication : public QObject
8484
/// any.
8585
server::ServerData m_serverdata;
8686

87+
/// True when connected to a DRO (Danganronpa Online) server.
88+
bool m_dro_mode = false;
89+
90+
/// Buffer for accumulating DRO CASEA character-list chunks before emitting SC.
91+
QStringList m_casea_buffer;
92+
8793
///////////////loading info///////////////////
8894

8995
// client ID. Not useful, to be removed eventually

src/packet_distribution.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,26 @@ void AOApplication::server_packet_received(AOPacket packet)
4242
}
4343
#endif
4444

45-
if (header == "decryptor")
45+
if (header == "DRO")
46+
{
47+
// DRO (Danganronpa Online) server handshake.
48+
// DRO servers send "DRO#<version>#%" instead of "decryptor#NOENCRYPT#%".
49+
m_dro_mode = true;
50+
m_casea_buffer.clear();
51+
m_serverdata.set_features(QStringList());
52+
QString f_hdid = get_hdid();
53+
send_server_packet(AOPacket("HI", {f_hdid}));
54+
log_to_demo = false;
55+
}
56+
else if (header == "decryptor")
4657
{
4758
if (content.size() == 0)
4859
{
4960
return;
5061
}
5162

63+
m_dro_mode = false;
64+
m_casea_buffer.clear();
5265
// default(legacy) values
5366
m_serverdata.set_features(QStringList());
5467

@@ -243,6 +256,26 @@ void AOApplication::server_packet_received(AOPacket packet)
243256
w_courtroom->character_loading_finished();
244257
}
245258
}
259+
else if (header == "CASEA")
260+
{
261+
// DRO servers deliver the character list in chunks via CASEA packets.
262+
// Accumulate all chunks; the final chunk is signalled by an empty content list.
263+
if (!is_courtroom_constructed())
264+
{
265+
return;
266+
}
267+
if (content.isEmpty())
268+
{
269+
// Final (empty) chunk — emit the accumulated list as a synthetic SC packet.
270+
AOPacket sc_packet("SC", m_casea_buffer);
271+
server_packet_received(sc_packet);
272+
m_casea_buffer.clear();
273+
}
274+
else
275+
{
276+
m_casea_buffer.append(content);
277+
}
278+
}
246279
else if (header == "SM")
247280
{
248281
if (!is_courtroom_constructed() || courtroom_loaded)
@@ -391,7 +424,13 @@ void AOApplication::server_packet_received(AOPacket packet)
391424
{
392425
if (is_courtroom_constructed() && courtroom_loaded)
393426
{
394-
w_courtroom->chatmessage_enqueue(packet.content());
427+
QStringList msg_content = packet.content();
428+
// DRO servers send up to 29 MS fields; AO2 courtroom only uses fields 0–14.
429+
if (m_dro_mode && msg_content.size() > 15)
430+
{
431+
msg_content = msg_content.mid(0, 15);
432+
}
433+
w_courtroom->chatmessage_enqueue(msg_content);
395434
}
396435
}
397436
else if (header == "MC")

0 commit comments

Comments
 (0)