Ejemplo n.º 1
0
/*
 * Each bot's entry point. 'arg' must be a dynamically allocated THREAD_ARG
 * pointer. The pointer is freed by the new thread.
 */
void*
BotEntryPoint(void *arg)
{
	THREAD_DATA *td = (THREAD_DATA*)arg;

	/* set the tls key */
	pthread_setspecific(g_tls_key, td);

	init_thread_data(td);

	db_instance_init();
	cmd_instance_init(td);
	player_instance_init(td);
	libman_instance_init(td);

	/* load the core pseudo-plugin */
	libman_load_library(td, NULL);

	/* load the bots libraries */
	int num_plugins = DelimCount(td->libstring, ' ') + 1;
	for (int i = 0; i < num_plugins; ++i) {
		char libname[64];
		DelimArgs(libname, 64, td->libstring, i, ' ', false);
		if (strlen(libname) > 0) {
			libman_load_library(td, libname);
		}
	}

	/* mainloop */
	mainloop(td);

	disconnect_from_server(td);

	libman_instance_shutdown(td);
	player_instance_shutdown(td);
	cmd_instance_shutdown(td);
	db_instance_shutdown();

	botman_bot_exiting(td->botman_handle);

	free_thread_data(td);

	free(td);

	return NULL;
}
Ejemplo n.º 2
0
static
void
mainloop(THREAD_DATA *td)
{
	THREAD_DATA::net_t *n = td->net;

	ticks_ms_t acc, ticks, lticks;	/* accumulator, current ticks, last iteration ticks */

	int	pktl;			/* packet length */
	uint8_t pkt[MAX_PACKET];	/* buffer space for a packet */

	if (connect_to_server(td) != 0) {
		free_thread_data(td);
		LogFmt(OP_MOD, "Error performing initial connect");
		return;
	}

	acc = 0;
	ticks = get_ticks_ms();
	lticks = ticks;
	ticks_ms_t last_botman_checkin = ticks;
	ticks_ms_t last_botman_stopcheck = ticks;
	ticks_ms_t last_config_mtime_check = ticks;
	while (td->running >= 0) {
		ticks = get_ticks_ms();
		acc += ticks - lticks;
		lticks = ticks;

		/* check in with the bot manager */
		if (ticks - last_botman_checkin >= BOTMAN_CHECKIN_INTERVAL) {
			botman_bot_checkin(td->botman_handle);
			last_botman_checkin = ticks;
		}
		if (ticks - last_botman_stopcheck >= BOTMAN_STOPCHECK_INTERVAL) {
			if (botman_bot_shouldstop(td->botman_handle)) {
				td->running = -1;
			}
			last_botman_stopcheck = ticks;
		}

		/* flush out tick events to bots */
		if (acc >= STEP_INTERVAL) { 
			libman_expire_timers(td);
			while(acc >= STEP_INTERVAL) {
				/* event_tick */
				libman_export_event(td, EVENT_TICK, NULL);
				acc -= STEP_INTERVAL;
			}
		}

		/* if the bot is disconnected, see if it is time to reconnect */
		if (n->state == NS_DISCONNECTED) {
			if (ticks - n->ticks->disconnected > 60000) {
				free_thread_data(td);
				init_thread_data(td);
				connect_to_server(td);
			} else {
				usleep(50000);	/* 50ms */
				continue;
			}
		}

		/* see if the config file has been modified and if so send a reread event */
		if (ticks - last_config_mtime_check >= CONFIG_MTIME_POLL_INTERVAL) {
			struct stat attr;
			memset(&attr, 0, sizeof(struct stat));
			if (stat(td->config->filename, &attr) == 0) {
				if (td->config->last_modified_time != attr.st_mtime) {
					libman_export_event(td, EVENT_CONFIG_CHANGE, NULL);
					td->config->last_modified_time = attr.st_mtime;
				}
			}
			last_config_mtime_check = ticks;
		}

		/* use up to STEP_INTERVAL ms for the db thread */
		ticks_ms_t ticks_taken = get_ticks_ms() - ticks;
		ticks_ms_t db_ticks = ticks_taken > STEP_INTERVAL ? STEP_INTERVAL : STEP_INTERVAL - ticks_taken;
		db_instance_export_events(db_ticks);

		/* read a packet or wait for a timeout */
		ticks_taken = get_ticks_ms() - ticks;
		ticks_ms_t timeout = ticks_taken > STEP_INTERVAL ? 0 : STEP_INTERVAL - ticks_taken;
		while (poll(n->pfd, 1, (int)timeout) > 0) {
			/* process incoming packet, data is waiting */
			pktl = (int)read(n->fd, pkt, MAX_PACKET);
			if (pktl >= 0) {
				++n->stats->packets_read;
				n->ticks->last_pkt_received = get_ticks_ms();

				if (n->encrypt->use_encryption) {
					if (pkt[0] == 0x00) {
						if (pktl >= 2) {
							decrypt_buffer(td, &pkt[2], pktl-2);
						}
					} else {
						decrypt_buffer(td, &pkt[1], pktl-1);
					}
				}

				if (td->debug->spew_packets) {
					spew_packet(pkt, pktl, DIR_INCOMING);
				}

				process_incoming_packet(td, pkt, pktl);
			}

			ticks_taken = get_ticks_ms() - ticks;
			timeout = timeout > ticks_taken ? timeout - ticks_taken : 0;
		}

		/* update the tick count after potential sleeping in poll() */
		ticks = get_ticks_ms();

		/* network state specfic actions */
		if (n->state == NS_CONNECTING) {
			/* retransmit connection request if it was lost */
			if (ticks - n->ticks->last_connection_request > 15000) {
				pkt_send_client_key(n->encrypt->client_key);
				n->ticks->last_connection_request = ticks;
			}
		} else if (ticks - n->ticks->last_pkt_received > 30*1000) {
			/* disconnect if no packets have been received for 30 seconds */
			Log(OP_MOD, "No data received for 30 seconds, reconnecting...");
			disconnect_from_server(td);
			continue;
		}

		/* transmit player position update if necessary */
		if (n->state == NS_CONNECTED && td->in_arena) {
			if ((ticks - n->ticks->last_pos_update_sent > 100
			    && td->bot_ship != SHIP_SPECTATOR)
			    || (ticks - n->ticks->last_pos_update_sent > 1000
			    && td->bot_ship == SHIP_SPECTATOR)) {
				pkt_send_position_update(td->bot_pos->x, td->bot_pos->y,
				    td->bot_vel->x, td->bot_vel->y);
				n->ticks->last_pos_update_sent = ticks;
			}
		}

		/* send periodic info/einfo */
		if (n->state == NS_CONNECTED) {
			// subtract 10000 to offset this by 10 seconds from *einfo to avoid filling buffers with commands/responses
			if (td->periodic->info && ticks - (td->periodic->last_info - 10000U) >= td->periodic->info) {
				int nhere = player_get_phere(td);
				PLAYER *parray = player_get_parray(td);
				for (int i = 0; i < nhere; ++i) {
					if (parray[i].here && td->enter->send_info) {
						PrivMessage(&parray[i], "*info");
					}
				}

				td->periodic->last_info = ticks;
			}

			if (td->periodic->einfo && ticks - td->periodic->last_einfo >= td->periodic->einfo) {
				int nhere = player_get_phere(td);
				PLAYER *parray = player_get_parray(td);
				for (int i = 0; i < nhere; ++i) {
					if (parray[i].here && td->enter->send_einfo) {
						PrivMessage(&parray[i], "*einfo");
					}
				}

				td->periodic->last_einfo = ticks;
			}
		}

		/* retransmit reliable packets that have not been acked */
		rpacket_list_t *l = n->rel_o->queue;
		rpacket_list_t::iterator iter = l->begin();
		while (iter != l->end()) {
			RPACKET *rp = *iter;
			if (ticks - rp->ticks > RELIABLE_RETRANSMIT_INTERVAL) {
				PACKET *p = allocate_packet(rp->len);
				memcpy(p->data, rp->data, rp->len);

				/* update packets retransmit tick */
				rp->ticks = ticks;

				queue_packet(p, SP_HIGH);
			}
			
			++iter;
		}

		/* free absent players if its time */
		ticks_ms_t flush_check_interval = 60 * 60 * 1000;
		if (ticks - td->arena->ticks->last_player_flush > flush_check_interval) {
			player_free_absent_players(td, flush_check_interval, true);
			td->arena->ticks->last_player_flush = ticks;
		}

		/* write packets generated during loop iteration */
		send_outgoing_packets(td);
	} /* while td->running != 0 */
}
Ejemplo n.º 3
0
int
main(int argc, char **argv)
{
	int		i;
        const char *getopt_str;
        int opt_p=0, opt_g=0, opt_a=0, opt_m=0;

        #if TEST_MPI
          init_test_mpi(&argc, &argv);
          getopt_str = "pgamlvtdi:";
        #else
          getopt_str = "pgalvtdi:";
        #endif

	GASNET_Safe(gasnet_init(&argc, &argv));
    	GASNET_Safe(gasnet_attach(htable, HANDLER_TABLE_SIZE,
		    TEST_SEGSZ_REQUEST, TEST_MINHEAPOFFSET));

        #if TEST_MPI
          #define TEST_MPI_USAGE  "  -m  use MPI calls                              \n"
        #else
          #define TEST_MPI_USAGE  ""
        #endif
        #if GASNET_PAR
          #define TEST_THREAD_USAGE " [<threads_per_node>]\n\n" \
	    "<threads_per_node> must be between 1 and "_STRINGIFY(TEST_MAXTHREADS)"       \n"
        #else
          #define TEST_THREAD_USAGE  "\n\n"
        #endif
	test_init("testthreads",0, "[ -pgalvtd ] [ -i <iters> ]"
            TEST_THREAD_USAGE
	    "no options means run all tests with "_STRINGIFY(DEFAULT_ITERS)" iterations\n"
	    "options:                                      \n"
	    "  -p  use puts                                   \n"
	    "  -g  use gets                                   \n"
	    "  -a  use Active Messages                        \n"
	    "  -l  use local Active Messages                  \n"
            TEST_MPI_USAGE
	    "  -v  output information about actions taken     \n"
	    "  -t  include AM handler actions with -v         \n"
	    "  -d  dynamic thread creation stress test        \n"
	    "  -i <iters> use <iters> iterations per thread   \n");

	while ((i = getopt (argc, argv, getopt_str)) != EOF) {
          switch (i) {
		case 'p': opt_p = 1; break;
		case 'g': opt_g = 1; break;
		case 'a': opt_a = 1; break;
                case 'm': opt_m = 1; break;
		case 'l': AM_loopback = 1; break;
		case 'i': iters = atoi(optarg); break;
                case 'v': verbose = 1; break;
                case 't': amtrace = 1; break;
                case 'd': threadstress = 1; break;
		default: test_usage();
          }
	}

        if (opt_p) test_functions[functions_num++] = test_put;
        if (opt_g) test_functions[functions_num++] = test_get;
        if (opt_a) {
          test_functions[functions_num++] = test_amshort;
          test_functions[functions_num++] = test_ammedium;
          test_functions[functions_num++] = test_amlong;
        }
        #if TEST_MPI
          if (opt_m) test_functions[functions_num++] = test_mpi;
        #endif
        if (amtrace) verbose = 1;

	/* Assume all test functions if no option is passed */
	if (functions_num  == 0) {
		MSG("running all functions!");
		
		memcpy(test_functions, test_functions_all, 
				sizeof(test_functions_all));
		functions_num = NUM_FUNCTIONS;
	}

	argc -= optind;

	if (argc > 1) test_usage();
	else if (argc == 1) {
		argv += optind;
		threads_num = atoi(argv[0]);
	}

	if (threads_num > TEST_MAXTHREADS || threads_num < 1) {
		printf("ERROR: Threads must be between 1 and %i\n",TEST_MAXTHREADS);
		exit(EXIT_FAILURE);
	}

        /* limit sizes to a reasonable size */
        #define LIMIT(sz) MIN(sz,4194304)
        { int sz = 0;
          sizes[sz++] = LIMIT(gasnet_AMMaxMedium()-1);
          sizes[sz++] = LIMIT(gasnet_AMMaxMedium());
          sizes[sz++] = LIMIT(gasnet_AMMaxMedium()+1);
          sizes[sz++] = LIMIT(gasnet_AMMaxLongRequest()-1);
          sizes[sz++] = LIMIT(gasnet_AMMaxLongRequest());
          sizes[sz++] = LIMIT(gasnet_AMMaxLongRequest()+1);
          sizes[sz++] = LIMIT(gasnet_AMMaxLongReply()-1);
          sizes[sz++] = LIMIT(gasnet_AMMaxLongReply());
          sizes[sz++] = LIMIT(gasnet_AMMaxLongReply()+1);
          assert(sizes[sz] == 0);
        }

	alloc_thread_data(threads_num);
        #if TEST_MPI
          attach_test_mpi();
        #endif

        #ifdef GASNET_PAR
          if (threadstress) {
            int spawniters = MAX(1,iters/threads_num);
            int i;
            MSG("Dynamic thread creation stress test, %d gasnet threads, (%d at a time)", spawniters*threads_num, threads_num);
            iters = 10; /* enough iters to ensure we get thread registration */
            for (i = 0; i < spawniters; i++) {
              test_createandjoin_pthreads(threads_num, &threadmain, tt_thread_data, sizeof(threaddata_t));
              TEST_PROGRESS_BAR(i, spawniters);
            }
          } else {
            MSG("Forking %d gasnet threads and running %d iterations", threads_num, iters);
            test_createandjoin_pthreads(threads_num, &threadmain, tt_thread_data, sizeof(threaddata_t));
          }
        #else /* for testmpi-seq and -parsync */
         #ifdef GASNET_SEQ
  	  MSG("Running with 1 thread/node for GASNET_SEQ mode");
         #else
  	  MSG("Running with 1 thread/node for GASNET_PARSYNC mode");
         #endif
          threadmain(tt_thread_data);
        #endif

        BARRIER();

	free_thread_data();

	MSG("Tests complete");

        BARRIER();

	gasnet_exit(0);

	return 0;
}