static void start_rabbit_game(CORE_DATA *cd, PLAYER *p, int bty, int special) { /* store rabbit's pid in user data */ ud(cd)->rabbit_pid = p->pid; /* announce the game to the arena */ ArenaMessageFmt("%s is now the rabbit!", p->name); ArenaMessageFmt("%s is now the rabbit!", p->name); ArenaMessageFmt("%s is now the rabbit!", p->name); /* announce the game to the player */ PrivMessageFmt(p, "You are now the rabbit! The game will end if you enter safe!"); /* prize the bounty/special */ PrivMessageFmt(p, "*prize %d", bty); if (special) PrivMessageFmt(p, "*prize #%d", special); /* * send *where to get rabbits location and set the bot to parse messages looking * for the response */ PrivMessage(p, "*where"); ud(cd)->expecting_where = 1; /* set the timer for the next *where to be sent */ ud(cd)->where_timer = SetTimer(SPAM_INTERVAL_MS, 0, 0); /* set the timer that signifies the game's end */ ud(cd)->gameover_timer = SetTimer(GAME_LENGTH_MS, 0, 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 */ }
void GameEvent(CORE_DATA *cd) { switch (cd->event) { case EVENT_START: RegisterPlugin(OPENCORE_VERSION, "rabbit", "cycad", "1.0", __DATE__, __TIME__, "A rabbit bot", sizeof(USER_DATA), 0); /* set rabbit pid to nobody */ ud(cd)->rabbit_pid = PID_NONE; /* register rabbit command */ RegisterCommand(COMMAND_RABBIT, "!rabbit", "Rabbit", 2, CMD_PRIVATE, "<name>:<bounty>[:<special>]", "Start the rabbit game", NULL); break; case EVENT_LOGIN: /* set the bot's location to center */ SetPosition(16 * 512, 16 * 512, 0, 0); break; case EVENT_COMMAND: switch (cd->cmd_id) { case COMMAND_RABBIT: { int show_usage = 0; PLAYER_ID rpid = ud(cd)->rabbit_pid; if (rpid == PID_NONE) { /* rabbit game is not runnig */ if (cd->cmd_argc <= 1) { show_usage = 1; } else { char *cmd = cd->cmd_argr[1]; int nargs = ArgCount(cmd, ':'); if (nargs != 2 && nargs != 3) { show_usage = 1; } else { /* args to command are valid */ /* copy name out of command */ char rname[20]; DelimArgs(rname, sizeof(rname), cmd, 0, ':', 0); /* copy bounty out of command */ int bty = AtoiArg(cmd, 1, ':'); /* copy special out of command if it was specified */ int special = 0; if (nargs == 3) special = AtoiArg(cmd, 2, ':'); /* find the player specified on the command line */ PLAYER *rabbit = FindPlayerName(rname, MATCH_HERE | MATCH_PREFIX); if (rabbit) { /* player found, start the game */ start_rabbit_game(cd, rabbit, bty, special); } else { RmtMessageFmt(cd->cmd_name, "Player not found: %s", rname); } } } } else { /* rabbit game is already running */ PLAYER *p = FindPlayerPid(rpid, MATCH_HERE); RmtMessageFmt(cd->cmd_name, "Rabbit is already running (%s is the rabbit)", p ? p->name : "**UNKNOWN**"); } /* display usage if necessary */ if (show_usage) RmtMessageFmt(cd->cmd_name, "Usage: %s <name>:<bounty>[:<special>]", cd->cmd_argv[0]); } default: break; } break; case EVENT_UPDATE: if (ud(cd)->rabbit_pid == cd->p1->pid) { /* if the update is for the rabbit */ if (cd->p1->status & STATUS_SAFE) { /* check if the rabbit is in safe */ /* the rabbit entered safe, end the game */ ArenaMessageFmt("%s has lost the rabbit game by entering safe!", cd->p1->name); ud(cd)->rabbit_pid = PID_NONE; } } break; case EVENT_TIMER: if (ud(cd)->rabbit_pid != PID_NONE) { /* if the rabbit game is running */ /* find the rabbit */ PLAYER *p = FindPlayerPid(ud(cd)->rabbit_pid, MATCH_HERE); if (p) { if (ud(cd)->where_timer == cd->timer_id) { /* a *where timer expired, send the next one and look for response */ PrivMessage(p, "*where"); ud(cd)->expecting_where = 1; } else if (ud(cd)->gameover_timer == cd->timer_id) { /* the game over timer expired, the rabbit didnt die and he won */ ArenaMessageFmt("%s wins the rabbit game by staying alive!", p->name); ud(cd)->rabbit_pid = PID_NONE; } } else { /* rabbit not found, this should never happen! */ } } case EVENT_MESSAGE: /* * Only look for a response of the game is running, the bot is expecting *where, * and the message type is an arena message. */ if (ud(cd)->rabbit_pid != PID_NONE && ud(cd)->expecting_where && cd->msg_type == MSG_ARENA) { /* message is a possible *where response */ /* find the rabbit */ PLAYER *p = FindPlayerPid(ud(cd)->rabbit_pid, MATCH_HERE); if (p) { char where_prefix[32]; snprintf(where_prefix, 32, "%s: ", p->name); where_prefix[32 - 1] = '\0'; /* * Verify that this is *where output by looking for "rabbit: " at the * beginning of the string. */ if (strncasecmp(where_prefix, cd->msg, strlen(where_prefix)) == 0) { /* this must be a *where response, process it */ char loc[4]; snprintf(loc, 4, "%s", &cd->msg[strlen(where_prefix)]); loc[4 - 1] = '\0'; /* announce the rabbits location */ ArenaMessageFmt(">>> Rabbit %s is at %-3s <<<", p->name, loc); /* set the next *where timer */ ud(cd)->where_timer = SetTimer(30 * 1000, 0, 0); /* * The bot wont be looking for *where responses until the * next time it sends the command, so turn parsing off * for now. */ ud(cd)->expecting_where = 0; } } } break; case EVENT_KILL: if (cd->p1->pid == ud(cd)->rabbit_pid) { /* the rabbit died */ ArenaMessageFmt("%s wins the rabbit game by killing Rabbit %s!", cd->p2->name, cd->p1->name); ud(cd)->rabbit_pid = PID_NONE; } break; case EVENT_LEAVE: if (ud(cd)->rabbit_pid == cd->p1->pid) { /* the rabbit left */ ArenaMessageFmt("%s has lost the rabbit game by leaving the arena!", cd->p1->name); ud(cd)->rabbit_pid = PID_NONE; } break; case EVENT_CHANGE: if (ud(cd)->rabbit_pid == cd->p1->pid) { /* the rabbit changed ship */ ArenaMessageFmt("%s has lost the rabbit game by changing ship/freq!", cd->p1->name); ud(cd)->rabbit_pid = PID_NONE; } break; } }