-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgame_server.cpp
More file actions
456 lines (415 loc) · 20.9 KB
/
game_server.cpp
File metadata and controls
456 lines (415 loc) · 20.9 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
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
#include "game_server.h"
#include "common.h"
#include "player.h"
#include "database.h"
#include <iostream>
#include <cstring>
#include "player.h"
using namespace std;
extern Room* startingRoom;
//TODO - Is there a better way to do the call back function?
static void (*cbs)(Player*, string);
static Player* (*logOnCheck)(string,string);
static int (*createNewUser)(string,string);
/* The ststic type in C tells the compiler that the function should not be accesible outside of this .cpp file
* This function takes an integer repersenting the socket, the buffer of characters to send, and the amount of
* characters in the buffer to send
*/
static void _send(SOCKET sock, const char *buffer, size_t size) {
int rs;
/* ignore on invalid socket */
if (sock == -1)
return;
/* send data */
while (size > 0) {
//TODO - Figure out why this uses send instead of telnet_printf()
if ((rs = send(sock, buffer, (int)size, 0)) == -1) {
if (errno != EINTR && errno != ECONNRESET) {
fprintf(stderr, "send() failed: %s\n", strerror(errno));
exit(1);
} else {
return;
}
} else if (rs == 0) {
fprintf(stderr, "send() unexpectedly returned 0\n");
exit(1);
}
/* update pointer and size to see if we've got more to send */
buffer += rs;
size -= rs;
}
}
//Struct that defines the kind of features our telnet supports
static const telnet_telopt_t telopts[] = {
{ TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DONT },
{ -1, 0, 0 }
};
/* process input line */
static void _online(const char *line, size_t overflow, void *ud) {
struct user_data *user = (struct user_data*)ud;
//int i;
(void)overflow;
//If the user has a state of -1 then they are selecting whether they want to log on or create an account
if (user->state == -1) {
if(strcmp(line, "n") == 0) {
telnet_printf(user->telnet, "Enter your username: ");
user->state = 0;
} else if(strcmp(line, "l") == 0) {
user->state = 1;
telnet_printf(user->telnet, "Enter your username : ");
}else{
telnet_printf(user->telnet, "Invalid option \nPlease enter an option (n for new account, l for log on) > ");
}
return;
//User is trying to create an account
} else if(user->state == 0) {
if(user->name == NULL) {
if(strcmp(line, "") == 0){
telnet_printf(user->telnet, "Invalid Username, please try again\nEnter your username: ");
return;
}
user->name = strdup(line);
telnet_printf(user->telnet, "Enter your password: ");
telnet_negotiate(user->telnet, TELNET_WILL, TELNET_TELOPT_ECHO);
return;
}
else {
if(strcmp(line, "") == 0){
telnet_printf(user->telnet, "Invalid Passowrd, please try again\nEnter your password: ");
return;
}
int userId = createNewUser(user->name, line);
if(userId == -1) {
telnet_printf(user->telnet, "\nA user already exists with this username, please enter a different one\nEnter your username: ");
telnet_negotiate(user->telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
free(user->name);
user->name = NULL;
return;
} else {
user->id = userId;
user->player = db->findPlayerById(user->id);
if(startingRoom == NULL)
cout << "STARTING ROOM IS NULL" << endl;
user->player->setRoom(startingRoom);
user->state = 2;
telnet_negotiate(user->telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
telnet_printf(user->telnet,"\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n/////////#&@@@&(///////////////////&@@@@%%///////////////////#&@@@@%%//////////////////#%%#(////////(##////////////////(%%#(////////(#%%(//////\n///////@@&/////@@&//////////////(@@(////#@@///////////////@@%%/////#@@///////////////@@%%#@@/////&@&(@@///////////////@@(@@/////(@@(@@//////\n///////@@///////@@//////////////@@(//////%%@%%/////////////#@&///////#@&//////////////@@//&@(////@@//@@///////////////@@/(@&////@@//@@//////\n///////@@///////////////////////@@(//////%%@%%/////////////#@&///////#@&//////////////@@///@@///&@(//@@///////////////@@//@@///(@&//@@//////\n///////@@&//////////////////////@@(//////%%@%%/////////////#@&///////#@&//////////////@@///&@(//@@///@@///////////////@@//(@&//@@///@@//////\n/////////#@@@@@@%%///////////////@@(((((%%@@@//////////////#@@%%%%%%%%%%%%%%&@&//////////////@@////@@/%%@(///@@///////////////@@///@@/(@&///@@//////\n////////////////@@//////////////@@((###(/////////////////#@&(((((((%%@&//////////////@@////%%@(@@////@@///////////////@@///(@&@@////@@//////\n//////(&(///////@@(/////////////@@(//////////////////////#@&///////#@&//////////////@@/////@@@(////@@///////////////@@////@@@&////@@//////\n//////%%@%%///////@@(/////////////@@(//////////////////////#@&///////#@&//////////////@@/////%%@@/////@@///////////////@@////(@@/////@@//////\n///////&@@@@@@@@@#//////////////@@(//////////////////////#@&///////#@&//////////////@@/////////////@@///////////////@@////////////@@//////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n");
telnet_printf(user->telnet, "Welcome To SPAMM\n");
return;
}
}
//The user is logging on
} else if(user->state == 1) {
if(user->name == NULL){
if(strlen(line) == 0){
telnet_printf(user->telnet, "Invalid username, please enter a valid username\nEnter your username : ");
return;
}
user->name = strdup(line);
telnet_printf(user->telnet, "Enter your password : ");
telnet_negotiate(user->telnet, TELNET_WILL, TELNET_TELOPT_ECHO);
return;
}
else {
//Password was entered, display newline to put new messges on the next line
telnet_printf(user->telnet, "\n");
if(strlen(line) == 0){
telnet_printf(user->telnet, "Please enter a password\n: ");
return;
}
Player* loggedOnUser = logOnCheck(user->name, line);
if (loggedOnUser == NULL){
telnet_printf(user->telnet, "Invalid username or password. Try again\nEnter Username: ");
free(user->name);
user->name = NULL;
telnet_negotiate(user->telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
return;
} else {
user->id = loggedOnUser->getID();
user->player = loggedOnUser;
//User is logged on, out of authentication state
//Get Player*
user->state = 2;
//Display Welcome Message
telnet_printf(user->telnet,"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n/////////#&@@@&(///////////////////&@@@@%%///////////////////#&@@@@%%//////////////////#%%#(////////(##////////////////(%%#(////////(#%%(//////\n///////@@&/////@@&//////////////(@@(////#@@///////////////@@%%/////#@@///////////////@@%%#@@/////&@&(@@///////////////@@(@@/////(@@(@@//////\n///////@@///////@@//////////////@@(//////%%@%%/////////////#@&///////#@&//////////////@@//&@(////@@//@@///////////////@@/(@&////@@//@@//////\n///////@@///////////////////////@@(//////%%@%%/////////////#@&///////#@&//////////////@@///@@///&@(//@@///////////////@@//@@///(@&//@@//////\n///////@@&//////////////////////@@(//////%%@%%/////////////#@&///////#@&//////////////@@///&@(//@@///@@///////////////@@//(@&//@@///@@//////\n/////////#@@@@@@%%///////////////@@(((((%%@@@//////////////#@@%%%%%%%%%%%%%%&@&//////////////@@////@@/%%@(///@@///////////////@@///@@/(@&///@@//////\n////////////////@@//////////////@@((###(/////////////////#@&(((((((%%@&//////////////@@////%%@(@@////@@///////////////@@///(@&@@////@@//////\n//////(&(///////@@(/////////////@@(//////////////////////#@&///////#@&//////////////@@/////@@@(////@@///////////////@@////@@@&////@@//////\n//////%%@%%///////@@(/////////////@@(//////////////////////#@&///////#@&//////////////@@/////%%@@/////@@///////////////@@////(@@/////@@//////\n///////&@@@@@@@@@#//////////////@@(//////////////////////#@&///////#@&//////////////@@/////////////@@///////////////@@////////////@@//////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n");
telnet_printf(user->telnet, "Welcome To SPAMM\n> ");
telnet_negotiate(user->telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
return;
}
}
}
//If the user has a name but no id, they have not been authenticated, this line is their password
// if (user->id == -1) {
// int userId = logOnCheck(user->name, line);
//
//Print newline so any message is not on the same line as the "Enter Password:" prompt
// telnet_printf(user->telnet, "\n");
// if(userId != -1){
// telnet_negotiate(user->telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
// telnet_printf(user->telnet,"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n/////////#&@@@&(///////////////////&@@@@%///////////////////#&@@@@%//////////////////#%#(////////(##////////////////(%#(////////(#%(//////\n///////@@&/////@@&//////////////(@@(////#@@///////////////@@%/////#@@///////////////@@%#@@/////&@&(@@///////////////@@(@@/////(@@(@@//////\n///////@@///////@@//////////////@@(//////%@%/////////////#@&///////#@&//////////////@@//&@(////@@//@@///////////////@@/(@&////@@//@@//////\n///////@@///////////////////////@@(//////%@%/////////////#@&///////#@&//////////////@@///@@///&@(//@@///////////////@@//@@///(@&//@@//////\n///////@@&//////////////////////@@(//////%@%/////////////#@&///////#@&//////////////@@///&@(//@@///@@///////////////@@//(@&//@@///@@//////\n/////////#@@@@@@%///////////////@@(((((%@@@//////////////#@@%%%%%%%%%%%%%%&@&//////////////@@////@@/%@(///@@///////////////@@///@@/(@&///@@//////\n////////////////@@//////////////@@((###(/////////////////#@&(((((((%@&//////////////@@////%@(@@////@@///////////////@@///(@&@@////@@//////\n//////(&(///////@@(/////////////@@(//////////////////////#@&///////#@&//////////////@@/////@@@(////@@///////////////@@////@@@&////@@//////\n//////%@%///////@@(/////////////@@(//////////////////////#@&///////#@&//////////////@@/////%@@/////@@///////////////@@////(@@/////@@//////\n///////&@@@@@@@@@#//////////////@@(//////////////////////#@&///////#@&//////////////@@/////////////@@///////////////@@////////////@@//////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n");
// telnet_printf(user->telnet, "Welcome To SPAMM\n> ");
// user->id = userId;
// return;
// } else {
// telnet_negotiate(user->telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
// telnet_printf(user->telnet, "Invalid Username or Password, Try again \nEnter Username: ");
// user->name = 0;
// return;
// }
//}
/* if line is "quit" then, well, quit */
if (strcmp(line, "quit") == 0) {
cout << user->name << " logged off " << endl;
close(user->sock);
user->sock = -1;
//_message(user->name, "** HAS QUIT **");
free(user->name);
user->name = 0;
user->id = -1;
user->state = -1;
return;
}
//If its not a log on or a quit command, pass it to the callback function for the game to figure out how to process it
cbs(user->player, line);
//Print the prompt for user input
telnet_printf(user->telnet, "> ");
}
static void linebuffer_push(char *buffer, size_t size, int *linepos,
char ch, void (*cb)(const char *line, size_t overflow, void *ud),
void *ud) {
/* CRLF -- line terminator */
if (ch == '\n' && *linepos > 0 && buffer[*linepos - 1] == '\r') {
/* NUL terminate (replaces \r in buffer), notify app, clear */
buffer[*linepos - 1] = 0;
cb(buffer, 0, ud);
*linepos = 0;
/* CRNUL -- just a CR */
} else if (ch == 0 && *linepos > 0 && buffer[*linepos - 1] == '\r') {
/* do nothing, the CR is already in the buffer */
/* anything else (including technically invalid CR followed by
* * anything besides LF or NUL -- just buffer if we have room
* * \r
* */
} else if (*linepos != (int)size) {
buffer[(*linepos)++] = ch;
/* buffer overflow */
} else {
/* terminate (NOTE: eats a byte), notify app, clear buffer */
buffer[size - 1] = 0;
cb(buffer, size - 1, ud);
*linepos = 0;
}
}
static void _input(struct user_data *user, const char *buffer,
size_t size) {
unsigned int i;
for (i = 0; i != size; ++i)
linebuffer_push(user->linebuffer, sizeof(user->linebuffer), &user->linepos,
(char)buffer[i], _online, user);
}
//Gets called when telnet has an event occur
static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
void *user_data) {
struct user_data *user = (struct user_data*)user_data;
switch (ev->type) {
/* data received */
case TELNET_EV_DATA:
_input(user, ev->data.buffer, ev->data.size);
break;
/* data must be sent */
case TELNET_EV_SEND:
_send(user->sock, ev->data.buffer, ev->data.size);
break;
/* enable compress2 if accepted by client */
case TELNET_EV_DO:
if (ev->neg.telopt == TELNET_TELOPT_COMPRESS2)
telnet_begin_compress2(telnet);
break;
/* error */
case TELNET_EV_ERROR:
close(user->sock);
user->sock = -1;
if (user->name != 0) {
//_message(user->name, "** HAS HAD AN ERROR **");
free(user->name);
user->name = 0;
}
telnet_free(user->telnet);
break;
default:
/* ignore */
break;
}
}
GameServer::GameServer(int portNumber) : portNum(portNumber) {
std::cout << "Attemping to listen on: " << portNum << std::endl;
//Changed from arrays to vectors, trying that change out
//Initalize all values of these struct arrays to 0
//memset(&pfd, 0, sizeof(pfd));
//memset(users, 0, sizeof(users));
//NOTE - I don't thing I need to do this because I am using a vector instead of an array
//Set the sockets of the users to -1, indicating that no user is currently connected
/*for (int i = 0; i != MAX_PLAYERS; ++i)
* users[i].sock = -1;
*/
/* Initalize a socket based on the linux socket man pagess
* AF_INET = This socket is expecting to listen to devices on the internet using IPV4 protocols
* SOCK_STREAM = Use this as a two way connection
*/
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return;
}
/* reuse address option */
//TODO - Figure out where the documentation for this is
rs = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&rs, sizeof(rs));
/* bind to listening addr/port */
memset(&addr, 0, sizeof(addr));
//Set the speficiations of the network socket
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
//Set the desired port number for the socket
addr.sin_port = htons(portNum);
//Attempt to bind the socket to the port
if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
fprintf(stderr, "bind() failed: %s\n", strerror(errno));
close(listen_sock);
return;
}
/* listen for clients */
if (listen(listen_sock, 5) == -1) {
fprintf(stderr, "listen() failed: %s\n", strerror(errno));
close(listen_sock);
return;
}
std::cout << "Listening successful" << std::endl;
//Initalize arrays
users = new user_data[MAX_PLAYERS];
pfd = new pollfd[MAX_PLAYERS + 1];
}
GameServer::GameServer() : GameServer(DEFAULT_TELNET_PORT){}
void GameServer::start() {
int returnStatus;
socklen_t addrlen;
int i;
SOCKET client_sock;
char buffer[512];
//Initalize the listening socket for incoming connections
pfd[MAX_PLAYERS].fd = listen_sock;
pfd[MAX_PLAYERS].events = POLLIN;
//Loop through and set all users sockets to -1 and their id to -1
for(i = 0; i != MAX_PLAYERS; ++i){
users[i].sock = -1;
users[i].id = -1;
users[i].player = NULL;
users[i].state = -1;
users[i].name = NULL;
users[i].linepos = 0;
}
//Loop forever and handle input
for(;;) {
/*Prepare for a socket connection to have data ready to recieve*/
for (i = 0; i != MAX_PLAYERS; ++i) {
if (users[i].sock != -1) {
pfd[i].fd = users[i].sock;
pfd[i].events = POLLIN;
} else {
pfd[i].fd = -1;
pfd[i].events = 0;
}
}
//Wait until any of the connected users create any events, or the 'listen_sock' has an event
returnStatus = poll(pfd, MAX_PLAYERS + 1, -1);
//Error
if (returnStatus == -1 && errno != EINTR) {
fprintf(stderr, "poll() failed: %s\n", strerror(errno));
close(listen_sock);
return;
}
//If the 'listen_socket' has data to read, meaning there is a new connection
if (pfd[MAX_PLAYERS].revents & POLLIN) {
/* accept the sock */
addrlen = sizeof(addr);
if ((client_sock = accept(listen_sock, (struct sockaddr *)&addr,
&addrlen)) == -1) {
fprintf(stderr, "accept() failed: %s\n", strerror(errno));
return;
}
//The new connection was successful
std::cout << "Connection received" << std::endl;
//Find an empty spot and add the socket to the array
/* find a free user */
for (i = 0; i != MAX_PLAYERS; ++i)
if (users[i].sock == -1)
break;
if (i == MAX_PLAYERS) {
printf(" rejected (too many users)\n");
_send(client_sock, "Too many users.\r\n", 14);
close(client_sock);
}
/* init, welcome */
users[i].sock = client_sock;
users[i].telnet = telnet_init(telopts, _event_handler, 0,
&users[i]);
telnet_negotiate(users[i].telnet, TELNET_WILL, TELNET_TELOPT_COMPRESS2);
telnet_negotiate(users[i].telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
telnet_printf(users[i].telnet, "Enter option (n for new account, l for log on)\n> ");
}
/* read from client */
for (i = 0; i != MAX_PLAYERS; ++i) {
/* skip users that aren't actually connected */
if (users[i].sock == -1)
continue;
if (pfd[i].revents & POLLIN) {
if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) {
//Push the received socket data into telnet and let the event handling take care of it
telnet_recv(users[i].telnet, buffer, rs);
} else if (rs == 0) {
printf("Connection closed.\n");
close(users[i].sock);
if (users[i].name != 0) {
//_message(users[i].name, "** HAS DISCONNECTED **");
free(users[i].name);
users[i].name = 0;
}
telnet_free(users[i].telnet);
users[i].sock = -1;
break;
} else if (errno != EINTR) {
fprintf(stderr, "recv(client) failed: %s\n",
strerror(errno));
exit(1);
}
}
}
}
}
void GameServer::setCallBackFunction(void (*f)(Player*,string)){
cbs = f;
}
void GameServer::setLogOnFunction(Player* (*f) (string,string)){
logOnCheck = f;
}
void GameServer::printToUser(Player *p, string message){
int i ;
message += "\n";
for(i = 0; i != MAX_PLAYERS; ++i)
if(users[i].player == p)
break;
telnet_printf(users[i].telnet, message.c_str());
}
void GameServer::printToUsers(vector<Player*> players, string message){
for(unsigned int i = 0; i < players.size(); ++i)
printToUser(players[i], message);
}
void GameServer::broadcast(std::string message) {
for(int i = 0; i != MAX_PLAYERS; ++i)
if(users[i].id != -1)
telnet_printf(users[i].telnet, message.c_str());
}
void GameServer::setCreateNewUserFunction(int (*f)(string,string)) {
createNewUser = f;
}
void GameServer::stop() {
}