1 /**
2  ** Server.cc - Server functions for Exult (NOT ExultStudio).
3  **
4  ** Written: 5/2/2001 - JSF
5  **/
6 
7 /*
8 Copyright (C) 2000-2013 The Exult Team
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 */
24 
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>   // All the ifdefs aren't useful if we don't do this
27 #endif
28 
29 // only if compiled with "exult studio support"
30 #ifdef USE_EXULTSTUDIO
31 
32 #include <unistd.h>
33 #include <fcntl.h>
34 
35 #if HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38 
39 #if HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #endif
42 
43 #if HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46 
47 #include <cstdio>
48 #include <cstdlib>
49 
50 #if HAVE_NETDB_H
51 #include <netdb.h>
52 #endif
53 
54 #ifndef _WIN32
55 #include <sys/un.h>
56 #endif
57 
58 #include "server.h"
59 #include "servemsg.h"
60 #include "utils.h"
61 #include "egg.h"
62 #include "barge.h"
63 #include "actors.h"
64 #include "gamewin.h"
65 #include "gamemap.h"
66 #include "chunkter.h"
67 #include "cheat.h"
68 #include "objserial.h"
69 #include "effects.h"
70 #include "Gump_manager.h"
71 
72 #ifdef USECODE_DEBUGGER
73 #include "debugserver.h"
74 #endif
75 
76 #ifdef _WIN32
77 #include "servewin32.h"
78 #include "cheat.h"
79 #endif
80 
81 #include <SDL_timer.h>
82 #include <SDL_events.h>
83 
84 using std::cout;
85 using std::cerr;
86 using std::endl;
87 /*
88  *  Sockets, etc.
89  */
90 int listen_socket = -1;         // Listen here for map-editor.
91 int client_socket = -1;         // Socket to the map-editor.
92 int highest_fd = -1;            // Largest fd + 1.
93 
94 /*
95  *  Set the 'highest_fd' value to 1 + <largest fd>.
96  */
Set_highest_fd()97 inline void Set_highest_fd(
98 ) {
99 	highest_fd = -1;       // Figure highest to listen to.
100 	if (listen_socket > highest_fd)
101 		highest_fd = listen_socket;
102 	if (client_socket > highest_fd)
103 		highest_fd = client_socket;
104 	highest_fd++;           // Select wants 1+highest.
105 }
106 
107 /*
108  *  Initialize server for map-editing.  Call this AFTER xfd has been set.
109  */
110 
Server_init()111 void Server_init(
112 ) {
113 	// Get location of socket file.
114 	std::string servename = get_system_path(EXULT_SERVER);
115 #ifndef _WIN32
116 	// Make sure it isn't there.
117 	unlink(servename.c_str());
118 #ifdef HAVE_GETADDRINFOX
119 	// Don't use the old deprecated network API
120 	int r;
121 	struct addrinfo hints, *ai;
122 
123 	memset(&hints, 0, sizeof(hints));
124 	hints.ai_flags = AI_PASSIVE;
125 	r = getaddrinfo(0,
126 	                servename.c_str(),
127 	                &hints,
128 	                &ai);
129 	if (r != 0) {
130 		cerr << "getaddrinfo(): " << gai_strerror(r) << endl;
131 		return;
132 	}
133 
134 	listen_socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
135 #else
136 	// Deprecated
137 	listen_socket = socket(PF_UNIX, SOCK_STREAM, 0);
138 #endif
139 	if (listen_socket < 0)
140 		perror("Failed to open map-editor socket");
141 	else {
142 #ifdef HAVE_GETADDRINFOX
143 		if (bind(listen_socket, ai->ai_addr, ai->ai_addrlen) == -1 ||
144 		        listen(listen_socket, 1) == -1)
145 #else
146 		struct sockaddr_un addr;
147 		addr.sun_family = AF_UNIX;
148 		strcpy(addr.sun_path, servename.c_str());
149 		if (bind(listen_socket, reinterpret_cast<struct sockaddr *>(&addr),
150 		         sizeof(addr.sun_family) + strlen(addr.sun_path) + 1) == -1 ||
151 		        listen(listen_socket, 1) == -1)
152 #endif
153 		{
154 			perror("Bind or listen on socket failed");
155 			close(listen_socket);
156 			listen_socket = -1;
157 		} else {        // Set to be non-blocking.
158 			cout << "Listening on socket " << listen_socket
159 			     << endl;
160 			fcntl(listen_socket, F_SETFL,
161 			      fcntl(listen_socket, F_GETFL) | O_NONBLOCK);
162 		}
163 #ifdef HAVE_GETADDRINFOX
164 		freeaddrinfo(ai);
165 #endif
166 	}
167 	Set_highest_fd();
168 #else
169 
170 	listen_socket = client_socket = -1;
171 	if (Exult_server::create_pipe(servename.c_str())) listen_socket = 1;
172 
173 #endif
174 }
175 
176 /*
177  *  Close the server.
178  */
179 
Server_close()180 void Server_close(
181 ) {
182 #ifdef _WIN32
183 	Exult_server::close_pipe();
184 	listen_socket = client_socket = -1;
185 #else
186 	// unlink socket file+++++++
187 	std::string servename = get_system_path(EXULT_SERVER);
188 	unlink(servename.c_str());
189 #endif
190 }
191 
192 
193 /*
194  *  A message from a client is available, so handle it.
195  */
196 
Handle_client_message(int & fd)197 static void Handle_client_message(
198     int &fd             // Socket to client.  May be closed.
199 ) {
200 	unsigned char data[Exult_server::maxlength];
201 	Exult_server::Msg_type id;
202 	int datalen = Exult_server::Receive_data(fd, id, data, sizeof(data));
203 	if (datalen < 0)
204 		return;
205 	const unsigned char *ptr = &data[0];
206 	Game_window *gwin = Game_window::get_instance();
207 	auto unhandled_message = [](Exult_server::Msg_type id) {
208 		cout << "Unhandled message received from Exult Studio (id = "
209 		     << id << ")" << endl;
210 	};
211 	switch (id) {
212 	case Exult_server::obj:
213 		Game_object::update_from_studio(&data[0], datalen);
214 		break;
215 	case Exult_server::barge:
216 		Barge_object::update_from_studio(&data[0], datalen);
217 		break;
218 	case Exult_server::egg:
219 		Egg_object::update_from_studio(&data[0], datalen);
220 		break;
221 	case Exult_server::container:
222 		Container_game_object::update_from_studio(&data[0], datalen);
223 		break;
224 	case Exult_server::npc:
225 		Actor::update_from_studio(&data[0], datalen);
226 		break;
227 	case Exult_server::info:
228 		Game_info_out(client_socket, Exult_server::version,
229 		              cheat.get_edit_lift(),
230 		              gwin->skip_lift,
231 		              cheat.in_map_editor(),
232 		              cheat.show_tile_grid(),
233 		              gwin->was_map_modified(),
234 		              static_cast<int>(cheat.get_edit_mode()));
235 		break;
236 	case Exult_server::write_map:
237 		gwin->write_map();  // Send feedback?+++++
238 		break;
239 	case Exult_server::read_map:
240 		gwin->read_map();
241 		break;
242 	case Exult_server::map_editing_mode: {
243 		int onoff = Read2(ptr);
244 		if ((onoff != 0) != cheat.in_map_editor())
245 			cheat.toggle_map_editor();
246 		break;
247 	}
248 	case Exult_server::tile_grid: {
249 		int onoff = Read2(ptr);
250 		if ((onoff != 0) != cheat.show_tile_grid())
251 			cheat.toggle_tile_grid();
252 		break;
253 	}
254 	case Exult_server::edit_lift: {
255 		int lift = Read2(ptr);
256 		cheat.set_edit_lift(lift);
257 		break;
258 	}
259 	case Exult_server::reload_usecode:
260 		gwin->reload_usecode();
261 		break;
262 	case Exult_server::locate_terrain: {
263 		int tnum = Read2(ptr);
264 		int cx = Read2s(ptr);
265 		int cy = Read2s(ptr);
266 		bool up = *ptr++ != 0;
267 		bool okay = gwin->get_map()->locate_terrain(tnum, cx, cy, up);
268 		unsigned char *wptr = &data[2];
269 		// Set back reply.
270 		Write2(wptr, cx);
271 		Write2(wptr, cy);
272 		wptr++;          // Skip 'up' flag.
273 		*wptr++ = okay ? 1 : 0;
274 		Exult_server::Send_data(client_socket,
275 		                        Exult_server::locate_terrain, data, wptr - data);
276 		break;
277 	}
278 	case Exult_server::swap_terrain: {
279 		int tnum = Read2(ptr);
280 		bool okay = Game_map::swap_terrains(tnum);
281 		unsigned char *wptr = &data[2];
282 		*wptr++ = okay ? 1 : 0;
283 		Exult_server::Send_data(client_socket,
284 		                        Exult_server::swap_terrain, data, wptr - data);
285 		break;
286 	}
287 	case Exult_server::insert_terrain: {
288 		int tnum = Read2s(ptr);
289 		bool dup = *ptr++ != 0;
290 		bool okay = Game_map::insert_terrain(tnum, dup);
291 		unsigned char *wptr = &data[3];
292 		*wptr++ = okay ? 1 : 0;
293 		Exult_server::Send_data(client_socket,
294 		                        Exult_server::insert_terrain, data, wptr - data);
295 		break;
296 	}
297 	case Exult_server::delete_terrain: {
298 		int tnum = Read2s(ptr);
299 		bool okay = Game_map::delete_terrain(tnum);
300 		unsigned char *wptr = &data[2];
301 		*wptr++ = okay ? 1 : 0;
302 		Exult_server::Send_data(client_socket,
303 		                        Exult_server::delete_terrain, data, wptr - data);
304 		break;
305 	}
306 	case Exult_server::send_terrain: {
307 		// Send back #, total, 512-bytes data.
308 		int tnum = Read2s(ptr);
309 		unsigned char *wptr = &data[2];
310 		Write2(wptr, gwin->get_map()->get_num_chunk_terrains());
311 		Chunk_terrain *ter = Game_map::get_terrain(tnum);
312 		// Serialize it.
313 		wptr += ter->write_flats(wptr, Game_map::is_v2_chunks());
314 		Exult_server::Send_data(client_socket,
315 		                        Exult_server::send_terrain, data, wptr - data);
316 		break;
317 	}
318 	case Exult_server::terrain_editing_mode: {
319 		// 1=on, 0=off, -1=undo.
320 		int onoff = Read2s(ptr);
321 		// skip_lift==0 <==> terrain-editing.
322 		gwin->skip_lift = onoff == 1 ? 0 : 256;
323 		static const char *msgs[3] = {"Terrain-Editing Aborted",
324 		                              "Terrain-Editing Done",
325 		                              "Terrain-Editing Enabled"
326 		                             };
327 		if (onoff == 0)     // End/commit.
328 			Game_map::commit_terrain_edits();
329 		else if (onoff == -1)
330 			Game_map::abort_terrain_edits();
331 		if (onoff >= -1 && onoff <= 1)
332 			gwin->get_effects()->center_text(msgs[onoff + 1]);
333 		gwin->set_all_dirty();
334 		break;
335 	}
336 	case Exult_server::set_edit_shape: {
337 		int shnum = Read2s(ptr);
338 		int frnum = Read2s(ptr);
339 		cheat.set_edit_shape(shnum, frnum);
340 		break;
341 	}
342 	case Exult_server::view_pos: {
343 		int tx = Read4(ptr);
344 		if (tx == -1) {     // This is a query?
345 			gwin->send_location();
346 			break;
347 		}
348 		int ty = Read4(ptr);
349 		// +++Later int txs = Read4(ptr);
350 		// int tys = Read4(ptr);
351 		// int scale = Read4(ptr);
352 		// Only set if chunk changed.
353 		if (tx / c_tiles_per_chunk !=
354 		        gwin->get_scrolltx() / c_tiles_per_chunk ||
355 		        ty / c_tiles_per_chunk !=
356 		        gwin->get_scrollty() / c_tiles_per_chunk) {
357 			gwin->set_scrolls(tx, ty);
358 			gwin->set_all_dirty();
359 		}
360 		break;
361 	}
362 	case Exult_server::set_edit_mode: {
363 		int md = Read2(ptr);
364 		if (md >= 0 && md <= 4)
365 			cheat.set_edit_mode(static_cast<Cheat::Map_editor_mode>(md));
366 		break;
367 	}
368 	case Exult_server::hide_lift: {
369 		int lift = Read2(ptr);
370 		gwin->skip_lift = lift;
371 		gwin->set_all_dirty();
372 		break;
373 	}
374 	case Exult_server::reload_shapes:
375 		Shape_manager::get_instance()->reload_shapes(Read2(ptr));
376 		break;
377 	case Exult_server::unused_shapes: {
378 		// Send back shapes not used in game.
379 		unsigned char data[Exult_server::maxlength];
380 		size_t sz = 1024u / 8;  // Gets bits for unused shapes.
381 		sz = sz > sizeof(data) ? sizeof(data) : sz;
382 		gwin->get_map()->find_unused_shapes(data, sz);
383 		Exult_server::Send_data(client_socket,
384 		                        Exult_server::unused_shapes, data, sz);
385 		break;
386 	}
387 	case Exult_server::locate_shape: {
388 		int shnum = Read2(ptr);
389 		int frnum = Read2s(ptr);
390 		int qual = Read2s(ptr);
391 		bool up = *ptr++ != 0;
392 		bool okay = gwin->locate_shape(shnum, up, frnum, qual);
393 		unsigned char *wptr = &data[6];     // Send back reply.
394 		wptr++;          // Skip 'up' flag.
395 		*wptr++ = okay ? 1 : 0;
396 		Exult_server::Send_data(client_socket,
397 		                        Exult_server::locate_shape, data, wptr - data);
398 		break;
399 	}
400 	case Exult_server::cut:     // Cut/copy.
401 		cheat.cut(*ptr != 0);
402 		break;
403 	case Exult_server::paste:
404 		cheat.paste();
405 		break;
406 	case Exult_server::npc_unused: {
407 		unsigned char *wptr = &data[0];
408 		Write2(wptr, gwin->get_num_npcs());
409 		Write2(wptr, gwin->get_unused_npc());
410 		Exult_server::Send_data(client_socket,
411 		                        Exult_server::npc_unused, data, wptr - data);
412 		break;
413 	}
414 	case Exult_server::npc_info: {
415 		int npcnum = Read2(ptr);
416 		Actor *npc = gwin->get_npc(npcnum);
417 		unsigned char *wptr = &data[2];
418 		if (npc) {
419 			Write2(wptr, npc->get_shapenum());
420 			*wptr++ = npc->is_unused();
421 			std::string nm = npc->get_npc_name();
422 			strcpy(reinterpret_cast<char *>(wptr), nm.c_str());
423 			// Point past ending nullptr.
424 			wptr += strlen(reinterpret_cast<char *>(wptr)) + 1;
425 		} else
426 			Write2(wptr, static_cast<unsigned short>(-1));
427 		Exult_server::Send_data(client_socket,
428 		                        Exult_server::npc_info, data, wptr - data);
429 		break;
430 	}
431 	case Exult_server::locate_npc:
432 		gwin->locate_npc(Read2(ptr));
433 		break;
434 	case Exult_server::edit_npc: {
435 		int npcnum = Read2(ptr);
436 		Actor *npc = gwin->get_npc(npcnum);
437 		if (npc)
438 			npc->edit();
439 		break;
440 	}
441 	case Exult_server::edit_selected: {
442 		unsigned char basic = *ptr;
443 		const Game_object_shared_vector &sel = cheat.get_selected();
444 		if (!sel.empty()) {
445 			if (basic)      // Basic obj. props?
446 				sel.back()->Game_object::edit();
447 			else
448 				sel.back()->edit();
449 		}
450 		break;
451 	}
452 	case Exult_server::set_edit_chunknum:
453 		cheat.set_edit_chunknum(Read2s(ptr));
454 		break;
455 	case Exult_server::game_pos: {
456 		Tile_coord pos = gwin->get_main_actor()->get_tile();
457 		unsigned char *wptr = &data[0];
458 		Write2(wptr, pos.tx);
459 		Write2(wptr, pos.ty);
460 		Write2(wptr, pos.tz);
461 		Exult_server::Send_data(client_socket,
462 		                        Exult_server::game_pos, data, wptr - data);
463 		break;
464 	}
465 	case Exult_server::goto_map: {
466 		char msg[80];
467 		Tile_coord pos = gwin->get_main_actor()->get_tile();
468 		int num = Read2(ptr);
469 		gwin->teleport_party(pos, true, num);
470 		sprintf(msg, "Map #%02x", num);
471 		gwin->get_effects()->center_text(msg);
472 		break;
473 	}
474 	case Exult_server::cont_show_gump: {
475 		Serial_in io(ptr);
476 		uintptr addr;
477 		io << addr;
478 		auto *p = reinterpret_cast<Game_object *>(addr);
479 		auto *obj = p->as_container();
480 		if (!obj) {
481 			cout << "Error decoding container data" << endl;
482 			break;
483 		}
484 		cout << "Displaying container's gump" << endl;
485 		Actor *act = obj->as_actor();
486 		if (act)
487 			act->show_inventory();
488 		else {
489 			// Avoid all frame-usecode and force-usecode subtleties.
490 			int gump = ShapeID::get_info(obj->get_shapenum()).get_gump_shape();
491 			if (gump >= 0) {
492 				Gump_manager *gump_man =
493 				    Game_window::get_instance()->get_gump_man();
494 				gump_man->add_gump(obj, gump);
495 			} else
496 				cerr << "The selected container has no gump!" << endl;
497 		}
498 		break;
499 	}
500 	case Exult_server::reload_shapes_info:
501 		Shape_manager::get_instance()->reload_shape_info();
502 		break;
503 	case Exult_server::usecode_debugging:
504 #ifdef USECODE_DEBUGGER
505 		Handle_debug_message(&data[0], datalen);
506 #else
507 		unhandled_message(id);
508 #endif
509 		break;
510 	default:
511 		unhandled_message(id);
512 	}
513 }
514 
515 /*
516  *  Delay for a fraction of a second, or until there's data available.
517  *  If a server request comes, it's handled here.
518  */
519 
520 #ifndef _WIN32
521 #define DELAY_TOTAL_MS 10
522 #define DELAY_SINGLE_MS 1
523 #endif
524 
Server_delay(Message_handler handle_message)525 void Server_delay(
526     Message_handler handle_message
527 ) {
528 #ifndef _WIN32
529 	Uint32 expiration = DELAY_TOTAL_MS + SDL_GetTicks();
530 	for (;;) {
531 		SDL_PumpEvents();
532 		if ((SDL_PeepEvents(nullptr, 0, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) != 0) ||
533 		    (static_cast<Sint32>(SDL_GetTicks()) >= static_cast<Sint32>(expiration))) return;
534 
535 		fd_set rfds;
536 		struct timeval timer;
537 		timer.tv_sec = 0;
538 		timer.tv_usec = DELAY_SINGLE_MS * 1000;      // Try 1/1000 second.
539 		FD_ZERO(&rfds);
540 		if (listen_socket >= 0)
541 			FD_SET(listen_socket, &rfds);
542 		if (client_socket >= 0)
543 			FD_SET(client_socket, &rfds);
544 		// Wait for timeout or event.
545 		if (select(highest_fd, &rfds, nullptr, nullptr, &timer) > 0) {
546 			// Something's come in.
547 			if (listen_socket >= 0 && FD_ISSET(listen_socket, &rfds)) {
548 				// New client connection.
549 				// For now, just one at a time.
550 				if (client_socket >= 0)
551 					close(client_socket);
552 				client_socket = accept(listen_socket, nullptr, nullptr);
553 				cout << "Accept returned client_socket = " <<
554 				     client_socket << endl;
555 				// Non-blocking.
556 				fcntl(client_socket, F_SETFL,
557 				      fcntl(client_socket, F_GETFL) | O_NONBLOCK);
558 				Set_highest_fd();
559 			}
560 			if (client_socket >= 0 && FD_ISSET(client_socket, &rfds)) {
561 				handle_message(client_socket);
562 				// Client gone?
563 				if (client_socket == -1)
564 					Set_highest_fd();
565 			}
566 		}
567 	}
568 #else
569 	if (listen_socket == -1) return;
570 
571 	if (client_socket == -1) {
572 		// Only do this in map edit mode
573 		if (!cheat.in_map_editor()) return;
574 
575 		std::string servename = get_system_path("<GAMEDAT>");
576 		if (!Exult_server::try_connect_to_client(servename.c_str())) return;
577 		else client_socket = 1;
578 		std::cout << "Connected to client" << endl;
579 	}
580 
581 	while (Exult_server::peek_pipe() > 0)
582 		handle_message(client_socket);
583 
584 	if (Exult_server::is_broken()) {
585 		std::cout << "Client disconnected." << endl;
586 		client_socket = -1;
587 	}
588 #endif
589 
590 }
591 
Server_delay()592 void Server_delay() {
593 	Server_delay(Handle_client_message);
594 }
595 
596 #endif
597