-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient_message.h
More file actions
146 lines (135 loc) · 5.55 KB
/
Copy pathclient_message.h
File metadata and controls
146 lines (135 loc) · 5.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
// client_message.h
// Covers:
// - split_slash() helper for splitting the message string into parts
// - parse_uint64() helper for parsing integers with error checking
// - build_message() function to parse the -m string and emit a binary datagram
*/
#ifndef CLIENT_MESSAGE_H
#define CLIENT_MESSAGE_H
#include "protocol.h"
#include "helpers.h"
#include <arpa/inet.h>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
/// Splits s by '/' and returns the resulting parts.
static std::vector<std::string> split_slash(const std::string &s) {
std::vector<std::string> parts;
std::string cur;
for (char c : s) {
if (c == '/') {
parts.push_back(cur);
cur.clear();
} else {
cur += c;
}
}
parts.push_back(cur);
return parts;
}
/// Parses a non-negative decimal integer from s into out.
/// Returns false if parsing fails or the value exceeds max_val.
static bool parse_uint64(const std::string &s, uint64_t max_val, uint64_t &out) {
if (s.empty()) return false;
char *end = nullptr;
errno = 0;
const unsigned long long v = std::strtoull(s.c_str(), &end, 10);
if (*end != '\0' || errno != 0) return false;
if (v > max_val) return false;
out = static_cast<uint64_t>(v);
return true;
}
// Message builder: parse the -m string and emit a binary datagram.
// Returns the number of bytes written to out_buf; dies on invalid input.
static size_t build_message(const std::string &msg_str, uint8_t *out_buf) {
const std::vector<std::string> parts = split_slash(msg_str);
if (parts.empty())
kaylescommon::die("invalid message string");
uint64_t type_val = 0;
if (!parse_uint64(parts[0], 255, type_val))
kaylescommon::die("invalid msg_type in -m: '%s'", parts[0].c_str());
const auto msg_type = static_cast<MsgType>(type_val);
switch (msg_type) {
case MsgType::join: {
// Fields: 0/<player_id>
if (parts.size() != 2)
kaylescommon::die("MSG_JOIN requires exactly 2 fields (0/<player_id>)");
uint64_t pid = 0;
if (!parse_uint64(parts[1], 0xFFFFFFFFULL, pid) || pid == 0)
kaylescommon::die("invalid player_id in MSG_JOIN: '%s'", parts[1].c_str());
MsgJoin m;
m.msg_type = static_cast<uint8_t>(MsgType::join);
m.player_id = htonl(static_cast<uint32_t>(pid));
std::memcpy(out_buf, &m, sizeof(m));
return sizeof(m);
}
case MsgType::move_one:
case MsgType::move_two: {
// Fields: 1/<player_id>/<game_id>/<pawn> or 2/…
if (parts.size() != 4)
kaylescommon::die("MSG_MOVE_* requires 4 fields (type/<player_id>/<game_id>/<pawn>)");
uint64_t pid = 0, gid = 0, pawn = 0;
if (!parse_uint64(parts[1], 0xFFFFFFFFULL, pid) || pid == 0)
kaylescommon::die("invalid player_id: '%s'", parts[1].c_str());
if (!parse_uint64(parts[2], 0xFFFFFFFFULL, gid))
kaylescommon::die("invalid game_id: '%s'", parts[2].c_str());
if (!parse_uint64(parts[3], 255, pawn))
kaylescommon::die("invalid pawn: '%s'", parts[3].c_str());
if (msg_type == MsgType::move_one) {
MsgMoveOne m;
m.msg_type = static_cast<uint8_t>(MsgType::move_one);
m.player_id = htonl(static_cast<uint32_t>(pid));
m.game_id = htonl(static_cast<uint32_t>(gid));
m.pawn = static_cast<uint8_t>(pawn);
std::memcpy(out_buf, &m, sizeof(m));
return sizeof(m);
} else {
MsgMoveTwo m;
m.msg_type = static_cast<uint8_t>(MsgType::move_two);
m.player_id = htonl(static_cast<uint32_t>(pid));
m.game_id = htonl(static_cast<uint32_t>(gid));
m.pawn = static_cast<uint8_t>(pawn);
std::memcpy(out_buf, &m, sizeof(m));
return sizeof(m);
}
}
case MsgType::keep_alive: {
// Fields: 3/<player_id>/<game_id>
if (parts.size() != 3)
kaylescommon::die("MSG_KEEP_ALIVE requires 3 fields (3/<player_id>/<game_id>)");
uint64_t pid = 0, gid = 0;
if (!parse_uint64(parts[1], 0xFFFFFFFFULL, pid) || pid == 0)
kaylescommon::die("invalid player_id: '%s'", parts[1].c_str());
if (!parse_uint64(parts[2], 0xFFFFFFFFULL, gid))
kaylescommon::die("invalid game_id: '%s'", parts[2].c_str());
MsgKeepAlive m;
m.msg_type = static_cast<uint8_t>(MsgType::keep_alive);
m.player_id = htonl(static_cast<uint32_t>(pid));
m.game_id = htonl(static_cast<uint32_t>(gid));
std::memcpy(out_buf, &m, sizeof(m));
return sizeof(m);
}
case MsgType::give_up: {
// Fields: 4/<player_id>/<game_id>
if (parts.size() != 3)
kaylescommon::die("MSG_GIVE_UP requires 3 fields (4/<player_id>/<game_id>)");
uint64_t pid = 0, gid = 0;
if (!parse_uint64(parts[1], 0xFFFFFFFFULL, pid) || pid == 0)
kaylescommon::die("invalid player_id: '%s'", parts[1].c_str());
if (!parse_uint64(parts[2], 0xFFFFFFFFULL, gid))
kaylescommon::die("invalid game_id: '%s'", parts[2].c_str());
MsgGiveUp m;
m.msg_type = static_cast<uint8_t>(MsgType::give_up);
m.player_id = htonl(static_cast<uint32_t>(pid));
m.game_id = htonl(static_cast<uint32_t>(gid));
std::memcpy(out_buf, &m, sizeof(m));
return sizeof(m);
}
default:
kaylescommon::die("unknown msg_type %u", static_cast<unsigned>(type_val));
}
}
#endif // CLIENT_MESSAGE_H