2021-03-17 19:07:40 +00:00
|
|
|
#include "servers/CNShardServer.hpp"
|
2020-12-02 09:00:11 +00:00
|
|
|
#include "PlayerManager.hpp"
|
2021-03-16 22:29:13 +00:00
|
|
|
#include "Chat.hpp"
|
2021-12-03 21:23:59 +00:00
|
|
|
#include "Email.hpp"
|
2021-03-17 19:07:40 +00:00
|
|
|
#include "servers/Monitor.hpp"
|
2020-12-02 22:42:33 +00:00
|
|
|
#include "settings.hpp"
|
2020-12-02 09:00:11 +00:00
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
static SOCKET listener;
|
2020-12-02 09:00:11 +00:00
|
|
|
static std::mutex sockLock; // guards socket list
|
2020-12-04 16:22:54 +00:00
|
|
|
static std::list<SOCKET> sockets;
|
2020-12-02 09:00:11 +00:00
|
|
|
static sockaddr_in address;
|
|
|
|
|
2020-12-04 16:22:54 +00:00
|
|
|
static bool transmit(std::list<SOCKET>::iterator& it, char *buff, int len) {
|
2020-12-02 09:00:11 +00:00
|
|
|
int n = 0;
|
|
|
|
int sock = *it;
|
|
|
|
|
|
|
|
while (n < len) {
|
2020-12-04 16:22:54 +00:00
|
|
|
n += send(sock, buff+n, len-n, 0);
|
|
|
|
if (SOCKETERROR(n)) {
|
2020-12-08 20:02:37 +00:00
|
|
|
printSocketError("send");
|
|
|
|
|
2020-12-04 16:22:54 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
shutdown(sock, SD_BOTH);
|
|
|
|
closesocket(sock);
|
|
|
|
#else
|
|
|
|
shutdown(sock, SHUT_RDWR);
|
2020-12-02 09:00:11 +00:00
|
|
|
close(sock);
|
2020-12-04 16:22:54 +00:00
|
|
|
#endif
|
2020-12-02 10:40:34 +00:00
|
|
|
|
|
|
|
std::cout << "[INFO] Disconnected a monitor" << std::endl;
|
|
|
|
|
2020-12-02 09:00:11 +00:00
|
|
|
it = sockets.erase(it);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-03 21:23:59 +00:00
|
|
|
/*
|
|
|
|
* The longest protocol message is for an email.
|
|
|
|
*
|
|
|
|
* message type + two formatted character names + the other formatting chars
|
|
|
|
* + the email title + the email body = ~1154
|
|
|
|
*
|
|
|
|
* The body can grow to twice its usual size (512) in the pathological case
|
|
|
|
* where every character is a newline.
|
|
|
|
*
|
|
|
|
* Multi-byte Unicode characters aren't a factor, as they should've been
|
|
|
|
* stripped out by sanitizeText().
|
|
|
|
*/
|
|
|
|
#define BUFSIZE 2048
|
|
|
|
|
|
|
|
static int process_email(char *buff, std::string email) {
|
|
|
|
strncpy(buff, "email ", 6);
|
|
|
|
int i = 6;
|
|
|
|
|
|
|
|
for (char c : email) {
|
|
|
|
if (i == BUFSIZE-2)
|
|
|
|
break;
|
|
|
|
|
|
|
|
buff[i++] = c;
|
|
|
|
|
|
|
|
// indent each line to prevent "endemail" spoofing
|
|
|
|
if (c == '\n')
|
|
|
|
buff[i++] = '\t';
|
|
|
|
}
|
|
|
|
|
|
|
|
buff[i++] = '\n';
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2021-03-16 21:06:10 +00:00
|
|
|
static void tick(CNServer *serv, time_t delta) {
|
2020-12-02 09:00:11 +00:00
|
|
|
std::lock_guard<std::mutex> lock(sockLock);
|
2021-12-03 21:23:59 +00:00
|
|
|
char buff[BUFSIZE];
|
2020-12-08 19:05:34 +00:00
|
|
|
int n;
|
2020-12-02 09:00:11 +00:00
|
|
|
|
|
|
|
auto it = sockets.begin();
|
2020-12-08 20:02:37 +00:00
|
|
|
outer:
|
2020-12-02 09:00:11 +00:00
|
|
|
while (it != sockets.end()) {
|
2020-12-02 10:40:34 +00:00
|
|
|
if (!transmit(it, (char*)"begin\n", 6))
|
2020-12-02 09:00:11 +00:00
|
|
|
continue;
|
|
|
|
|
2020-12-15 14:58:11 +00:00
|
|
|
// player
|
2020-12-02 09:00:11 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-12-04 12:04:59 +00:00
|
|
|
if (pair.second->hidden)
|
|
|
|
continue;
|
|
|
|
|
2020-12-08 19:05:34 +00:00
|
|
|
n = std::snprintf(buff, sizeof(buff), "player %d %d %s\n",
|
2020-12-02 09:00:11 +00:00
|
|
|
pair.second->x, pair.second->y,
|
|
|
|
PlayerManager::getPlayerName(pair.second, false).c_str());
|
|
|
|
|
2020-12-02 10:40:34 +00:00
|
|
|
if (!transmit(it, buff, n))
|
2020-12-08 19:05:34 +00:00
|
|
|
goto outer;
|
2020-12-02 09:00:11 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 14:58:11 +00:00
|
|
|
// chat
|
2021-03-16 22:29:13 +00:00
|
|
|
for (auto& str : Chat::dump) {
|
2020-12-15 14:58:11 +00:00
|
|
|
n = std::snprintf(buff, sizeof(buff), "chat %s\n", str.c_str());
|
|
|
|
|
|
|
|
if (!transmit(it, buff, n))
|
|
|
|
goto outer;
|
|
|
|
}
|
|
|
|
|
2021-12-03 21:23:59 +00:00
|
|
|
// emails
|
|
|
|
for (auto& str : Email::dump) {
|
|
|
|
n = process_email(buff, str);
|
|
|
|
|
|
|
|
if (!transmit(it, buff, n))
|
|
|
|
goto outer;
|
|
|
|
|
|
|
|
if (!transmit(it, (char*)"endemail\n", 9))
|
|
|
|
goto outer;
|
|
|
|
}
|
|
|
|
|
2020-12-02 10:40:34 +00:00
|
|
|
if (!transmit(it, (char*)"end\n", 4))
|
2020-12-02 09:00:11 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
it++;
|
|
|
|
}
|
2020-12-15 14:58:11 +00:00
|
|
|
|
2021-03-16 22:29:13 +00:00
|
|
|
Chat::dump.clear();
|
2021-12-03 21:23:59 +00:00
|
|
|
Email::dump.clear();
|
2020-12-02 09:00:11 +00:00
|
|
|
}
|
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
bool Monitor::acceptConnection(SOCKET fd, uint16_t revents) {
|
2020-12-02 09:00:11 +00:00
|
|
|
socklen_t len = sizeof(address);
|
|
|
|
|
2020-12-04 16:30:21 +00:00
|
|
|
if (!settings::MONITORENABLED)
|
2020-12-06 00:44:37 +00:00
|
|
|
return false;
|
2020-12-04 16:30:21 +00:00
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
if (fd != listener)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (revents & ~POLLIN) {
|
|
|
|
std::cout << "[FATAL] Error on monitor listener?" << std::endl;
|
2020-12-06 04:25:23 +00:00
|
|
|
terminate(0);
|
2020-12-06 00:44:37 +00:00
|
|
|
}
|
2020-12-02 09:00:11 +00:00
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
int sock = accept(listener, (struct sockaddr*)&address, &len);
|
2020-12-08 20:02:37 +00:00
|
|
|
if (SOCKETERROR(sock)) {
|
|
|
|
printSocketError("accept");
|
2020-12-06 00:44:37 +00:00
|
|
|
return true;
|
2020-12-08 20:02:37 +00:00
|
|
|
}
|
2020-12-05 22:16:09 +00:00
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
setSockNonblocking(listener, sock);
|
2020-12-02 09:00:11 +00:00
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
std::cout << "[INFO] New monitor connection from " << inet_ntoa(address.sin_addr) << std::endl;
|
2020-12-02 09:00:11 +00:00
|
|
|
|
2020-12-06 00:44:37 +00:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(sockLock);
|
|
|
|
|
|
|
|
sockets.push_back(sock);
|
2020-12-02 09:00:11 +00:00
|
|
|
}
|
2020-12-06 00:44:37 +00:00
|
|
|
|
|
|
|
return true;
|
2020-12-02 09:00:11 +00:00
|
|
|
}
|
2021-03-16 21:06:10 +00:00
|
|
|
|
|
|
|
SOCKET Monitor::init() {
|
|
|
|
listener = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (SOCKETERROR(listener)) {
|
|
|
|
std::cout << "Failed to create monitor socket" << std::endl;
|
|
|
|
printSocketError("socket");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
const char opt = 1;
|
|
|
|
#else
|
|
|
|
int opt = 1;
|
|
|
|
#endif
|
|
|
|
if (SOCKETERROR(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))) {
|
|
|
|
std::cout << "Failed to set SO_REUSEADDR on monitor socket" << std::endl;
|
|
|
|
printSocketError("setsockopt");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
address.sin_family = AF_INET;
|
|
|
|
address.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
address.sin_port = htons(settings::MONITORPORT);
|
|
|
|
|
|
|
|
if (SOCKETERROR(bind(listener, (struct sockaddr*)&address, sizeof(address)))) {
|
|
|
|
std::cout << "Failed to bind to monitor port" << std::endl;
|
|
|
|
printSocketError("bind");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SOCKETERROR(listen(listener, SOMAXCONN))) {
|
|
|
|
std::cout << "Failed to listen on monitor port" << std::endl;
|
|
|
|
printSocketError("listen");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
unsigned long mode = 1;
|
|
|
|
if (ioctlsocket(listener, FIONBIO, &mode) != 0) {
|
|
|
|
#else
|
|
|
|
if (fcntl(listener, F_SETFL, (fcntl(listener, F_GETFL, 0) | O_NONBLOCK)) != 0) {
|
|
|
|
#endif
|
|
|
|
std::cerr << "[FATAL] OpenFusion: fcntl failed" << std::endl;
|
|
|
|
printSocketError("fcntl");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Monitor listening on *:" << settings::MONITORPORT << std::endl;
|
|
|
|
|
|
|
|
REGISTER_SHARD_TIMER(tick, settings::MONITORINTERVAL);
|
|
|
|
|
|
|
|
return listener;
|
|
|
|
}
|