RelayServer.cpp 4.26 KB
Newer Older
Hubert Denkmair's avatar
Hubert Denkmair committed
1
2
#include "RelayServer.h"
#include <iostream>
Hubert Denkmair's avatar
Hubert Denkmair committed
3
#include <string>
Hubert Denkmair's avatar
Hubert Denkmair committed
4
#include <TcpServer/EPoll.h>
5
#include "JsonProtocol.h"
Hubert Denkmair's avatar
Hubert Denkmair committed
6
7
8
9
10
11
12

RelayServer::RelayServer()
{
}

int RelayServer::Run()
{
Hubert Denkmair's avatar
Hubert Denkmair committed
13
	uWS::Hub h;
Hubert Denkmair's avatar
Hubert Denkmair committed
14
15
	EPoll epoll;

16
17
18
19
20
21
22
	const char* gameserverHost = getEnvOrDefault(ENV_GAMESERVER_HOST, ENV_GAMESERVER_HOST_DEFAULT);
	const char* gameserverPort = getEnvOrDefault(ENV_GAMESERVER_PORT, ENV_GAMESERVER_PORT_DEFAULT);
	const char* websocketPort = getEnvOrDefault(ENV_WEBSOCKET_PORT, ENV_WEBSOCKET_PORT_DEFAULT);

	fprintf(stderr, "connecting to gameserver on %s port %s...\n", gameserverHost , gameserverPort);
	_clientSocket = connectTcpSocket(gameserverHost , gameserverPort);
	if (_clientSocket < 0)
Hubert Denkmair's avatar
Hubert Denkmair committed
23
24
25
26
	{
		perror("connect to server failed");
		return -1;
	}
27
	fprintf(stderr, "connected.\n");
Hubert Denkmair's avatar
Hubert Denkmair committed
28

Hubert Denkmair's avatar
Hubert Denkmair committed
29
30
31
	_tcpProtocol.SetFrameCompleteCallback(
		[this, &h](uint64_t frame_id)
		{
32
			auto &logMessages = _tcpProtocol.GetPendingLogItems();
Hubert Denkmair's avatar
Hubert Denkmair committed
33
			h.getDefaultGroup<uWS::SERVER>().forEach(
34
				[this, frame_id, &logMessages](uWS::WebSocket<uWS::SERVER>* sock)
Hubert Denkmair's avatar
Hubert Denkmair committed
35
				{
Hubert Denkmair's avatar
Hubert Denkmair committed
36
37
					auto con = static_cast<WebsocketConnection*>(sock->getUserData());
					con->FrameComplete(frame_id, _tcpProtocol);
38
39

					auto key = con->getViewerKey();
40
					if (logMessages.find(key) != logMessages.end())
41
					{
42
43
44
45
						for (auto& item: logMessages.at(key))
						{
							con->LogMessage(frame_id, item.message);
						}
46
					}
Hubert Denkmair's avatar
Hubert Denkmair committed
47
				}
Hubert Denkmair's avatar
Hubert Denkmair committed
48
			);
49
			_tcpProtocol.ClearLogItems();
50
51
52
53
54
55

			for (auto& msg: _tcpProtocol.GetPendingMessages())
			{
				std::string s = json(*msg).dump();
				h.getDefaultGroup<uWS::SERVER>().broadcast(s.data(), s.length(), uWS::OpCode::TEXT);
			}
Hubert Denkmair's avatar
Hubert Denkmair committed
56
57
58
		}
	);
	epoll.AddFileDescriptor(_clientSocket, EPOLLIN|EPOLLPRI|EPOLLERR);
Hubert Denkmair's avatar
Hubert Denkmair committed
59

Hubert Denkmair's avatar
Hubert Denkmair committed
60
61
	h.onConnection(
		[](uWS::WebSocket<uWS::SERVER> *ws, uWS::HttpRequest req)
Hubert Denkmair's avatar
Hubert Denkmair committed
62
		{
Hubert Denkmair's avatar
Hubert Denkmair committed
63
			ws->setUserData(new WebsocketConnection(ws));
Hubert Denkmair's avatar
Hubert Denkmair committed
64
65
		}
	);
Hubert Denkmair's avatar
Hubert Denkmair committed
66
67
	h.onDisconnection(
		[](uWS::WebSocket<uWS::SERVER> *ws, int code, const char *message, size_t length)
Hubert Denkmair's avatar
Hubert Denkmair committed
68
		{
Hubert Denkmair's avatar
Hubert Denkmair committed
69
70
			auto *con = static_cast<WebsocketConnection*>(ws->getUserData());
			delete con;
Hubert Denkmair's avatar
Hubert Denkmair committed
71
72
73
		}
	);

Hubert Denkmair's avatar
Hubert Denkmair committed
74
	h.onMessage([](uWS::WebSocket<uWS::SERVER> *ws, char *message, size_t length, uWS::OpCode opCode)
Hubert Denkmair's avatar
Hubert Denkmair committed
75
76
77
	{	
		if (length>MAX_CLIENT_MESSAGE_SIZE)
		{
78
			ws->close(413, "payload to large");
Hubert Denkmair's avatar
Hubert Denkmair committed
79
80
81
			return;
		}

82
83
84
85
86
		try {
			auto *con = static_cast<WebsocketConnection*>(ws->getUserData());
			std::string s(message, length);
			json data = json::parse(s, nullptr, false);
			if (!data.is_object()) { return; }
Hubert Denkmair's avatar
Hubert Denkmair committed
87

88
89
90
91
92
93
94
			if (data["viewer_key"].is_string())
			{
				std::string key = data["viewer_key"];
				con->setViewerKey(static_cast<uint64_t>(std::stol(key)));
			}
		}
		catch (std::exception e)
Hubert Denkmair's avatar
Hubert Denkmair committed
95
		{
96
97
			ws->close(418, "invalid request");
			return;
Hubert Denkmair's avatar
Hubert Denkmair committed
98
		}
Hubert Denkmair's avatar
Hubert Denkmair committed
99
	});
Hubert Denkmair's avatar
Hubert Denkmair committed
100

Hubert Denkmair's avatar
Hubert Denkmair committed
101
102
	std::string response = "Hello!";
	h.onHttpRequest([&](uWS::HttpResponse *res, uWS::HttpRequest req, char *data, size_t length, size_t remainingBytes)
Hubert Denkmair's avatar
Hubert Denkmair committed
103
	{
Hubert Denkmair's avatar
Hubert Denkmair committed
104
105
		res->end(response.data(), response.length());
	});
Hubert Denkmair's avatar
Hubert Denkmair committed
106

107
	if (!h.listen(atoi(websocketPort)))
Hubert Denkmair's avatar
Hubert Denkmair committed
108
	{
Hubert Denkmair's avatar
Hubert Denkmair committed
109
		return -1;
Hubert Denkmair's avatar
Hubert Denkmair committed
110
	}
Hubert Denkmair's avatar
Hubert Denkmair committed
111

Hubert Denkmair's avatar
Hubert Denkmair committed
112
	epoll.AddFileDescriptor(h.getLoop()->getEpollFd(), EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLRDHUP|EPOLLHUP); // TODO check which events are neccessary
113
114
	bool shouldRun = true;
	while (shouldRun)
Hubert Denkmair's avatar
Hubert Denkmair committed
115
	{
Hubert Denkmair's avatar
Hubert Denkmair committed
116
		epoll.Poll(1000,
117
			[this, &h, &shouldRun](const epoll_event& ev)
Hubert Denkmair's avatar
Hubert Denkmair committed
118
119
120
			{
				if (ev.data.fd == _clientSocket)
				{
121
122
					shouldRun = _tcpProtocol.Read(_clientSocket);
					return shouldRun;
Hubert Denkmair's avatar
Hubert Denkmair committed
123
124
125
126
127
128
129
130
				}
				else
				{
					h.poll();
				}
				return true;
			}
		);
Hubert Denkmair's avatar
Hubert Denkmair committed
131
	}
132
133

	return -2;
Hubert Denkmair's avatar
Hubert Denkmair committed
134
}
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

int RelayServer::connectTcpSocket(const char *hostname, const char *port)
{
	struct addrinfo hints;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
	hints.ai_socktype = SOCK_STREAM; /* Datagram socket */

	struct addrinfo *result;
	int s = getaddrinfo(hostname, port, &hints, &result);
	if (s != 0)
	{
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
		return s;
	}

	struct addrinfo* rp = nullptr;
	int retval = -1;
	for (rp=result; rp!=nullptr; rp=rp->ai_next)
	{
		int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
		if (fd == -1) { continue; }

		if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0)
		{
			retval = fd;
			break;
		}
		close(fd);
	}

	freeaddrinfo(result);
	return retval;
}

const char *RelayServer::getEnvOrDefault(const char *envVar, const char *defaultValue)
{
	const char* value = getenv(envVar);
	if (value == nullptr)
	{
		value = defaultValue;
	}
	return value;
}