//--------- Begin of function NationArray::del_nation ---------// // // <int> recNo = the no. of the record to be deleted // (default : current record no.) // void NationArray::del_nation(int recNo) { //-----------------------------------------// Nation* nationPtr = nation_array[recNo]; // operator[] will check for deleted nation error, can't use operator() because it use firm_array()->nation_recno nation_count--; if( nationPtr->nation_type == NATION_AI ) ai_nation_count--; last_del_nation_date = info.game_date; //------ delete and linkout the nation ------// nationPtr->deinit(); delete nationPtr; go(recNo); *( (Nation**) get() ) = NULL; // Nullify the pointer linkout(); //--- if the nation to be deleted is the player's nation ---// // ####### begin Gilbert 26/7 #########// if( recNo==player_recno ) { player_ptr = NULL; player_recno = 0; if( !sys.signal_exit_flag ) sys.set_view_mode(MODE_NORMAL); // set the view mode to normal mode to prevent possible problems } if( !sys.quick_exit_flag() ) { //---------- update statistic ----------// update_statistic(); // as max_overall_nation_recno and others may be pointing to the deleted nation } // ####### end Gilbert 26/7 #########// }
//--------- Begin of function NationArray::new_nation ---------// // // <int> nationClassId = NATION_???, defined in ONATION2.H // <int> nationType = NATION_OWN, NATION_AI, or NATION_REMOTE // <int> raceId = id. of the race // <int> colorSchemeId = the color scheme id. of the nation // [unsigned long] dpPlayerId = DirectPlayer player id. (only for multiplayer game) // // // return : <int> nationRecno = the recno. of the newly added nation // int NationArray::new_nation(int nationClassId, int nationType, int raceId, int colorSchemeId, unsigned long dpPlayerId) { //--------------------------------------------------------// int nationRecno = create_nation(nationClassId); Nation* nationPtr = nation_array[nationRecno]; if( nationType == NATION_OWN ) { player_ptr = nationPtr; // for ~nation_array to quick access player_recno = nationRecno; info.default_viewing_nation_recno = nationRecno; info.viewing_nation_recno = nationRecno; } //--- we must call init() after setting ai_type & nation_res_id ----// nationPtr->init(nationType, raceId, colorSchemeId, dpPlayerId); //--- store the colors of all nations into a single array for later fast access ---// nation_color_array[nationPtr->nation_recno] = nationPtr->nation_color; nation_power_color_array[nationPtr->nation_recno] = nationPtr->nation_color; // use a lighter color for the nation power area //-------------------------------------------// nation_count++; if( nationType == NATION_AI ) ai_nation_count++; last_new_nation_date = info.game_date; //---------- update statistic ----------// update_statistic(); return nationRecno; }
//--------- Begin of function NationArray::next_month ---------// // void NationArray::next_month() { int i; Nation *nationPtr; LOG_MSG("begin NationArray::next_month"); for( i=size() ; i>0 ; i-- ) { nationPtr = (Nation*) get_ptr(i); if( nationPtr ) // nationPtr == NULL, if it's deleted { LOG_MSG(i); nationPtr->next_month(); LOG_MSG(misc.get_random_seed() ); } } LOG_MSG("end NationArray::next_month"); //------- update statistic -----------// update_statistic(); }
//--------- Begin of function NationArray::new_nation ---------// // // used in creating human players in multi-player game // return : <int> nationRecno = the recno. of the newly added nation // int NationArray::new_nation(NewNationPara& nationPara) { err_when( info.game_date < last_del_nation_date + NEW_NATION_INTERVAL_DAYS ); //--------------------------------------------------------// int nationClassId; if( nationPara.race_id > 0 ) nationClassId = NATION_HUMAN; else nationClassId = NATION_MONSTER; int nationRecno = create_nation(nationClassId); Nation* nationPtr = nation_array[nationRecno]; err_when( nationRecno != nationPara.nation_recno ); err_when( !remote.is_enable() ); char nationType = nationPara.dp_player_id == remote.self_player_id() ? NATION_OWN : NATION_REMOTE; if( nationType == NATION_OWN ) { player_ptr = nationPtr; // for ~nation_array to quick access player_recno = nationRecno; info.default_viewing_nation_recno = nationRecno; info.viewing_nation_recno = nationRecno; } //--- we must call init() after setting ai_type & nation_res_id ----// nationPtr->init(nationType, nationPara.race_id, nationPara.color_scheme, nationPara.dp_player_id); if( nationPara.use_gem_stones ) { nationPtr->init_cash( nationPara.use_gem_stones ); game.total_gem_stones += nationPara.use_gem_stones; // do not reset use_gemstones, as init_cash is called again in battle // update player profile if( nationPtr->is_own() ) { player_profile.reload(); if( player_profile.gem_stones >= nationPara.use_gem_stones ) player_profile.gem_stones -= nationPara.use_gem_stones; else player_profile.gem_stones = 0; player_profile.save(); } } if( nationPara.ranking > game.max_ranking ) { game.max_ranking = nationPara.ranking; } game.player_ranking[nationPtr->nation_recno-1] = nationPara.ranking; //--- store the colors of all nations into a single array for later fast access ---// nation_color_array[nationPtr->nation_recno] = nationPtr->nation_color; nation_power_color_array[nationPtr->nation_recno] = nationPtr->nation_color; // use a lighter color for the nation power area //-------------------------------------------// nation_count++; if( nationType == NATION_AI ) ai_nation_count++; // ####### begin Gilbert 25/5 ##########// last_new_nation_date = info.game_date; // ####### end Gilbert 25/5 ##########// //---------- update statistic ----------// update_statistic(); return nationRecno; }
// Main test function. Locates the given magic pixel pattern on the screen, then // runs one full latency test, sending input events and recording responses. On // success, the results of the test are reported in the output parameters, and // true is returned. If the test fails, the error parameter is filled in with // an error message and false is returned. bool measure_latency( const uint8_t magic_pattern[], double *out_key_down_latency_ms, double *out_scroll_latency_ms, double *out_max_js_pause_time_ms, double *out_max_css_pause_time_ms, double *out_max_scroll_pause_time_ms, char **error) { screenshot *screenshot = take_screenshot(0, 0, UINT32_MAX, UINT32_MAX); if (!screenshot) { *error = "Failed to take screenshot."; return false; } assert(screenshot->width > 0 && screenshot->height > 0); size_t x, y; bool found_pattern = find_pattern(magic_pattern, screenshot, &x, &y); free_screenshot(screenshot); if (!found_pattern) { *error = "Failed to find test pattern on screen."; return false; } uint8_t full_pattern[pattern_bytes]; for (int i = 0; i < pattern_magic_bytes; i++) { full_pattern[i] = magic_pattern[i]; } measurement_t measurement; measurement_t previous_measurement; memset(&measurement, 0, sizeof(measurement_t)); memset(&previous_measurement, 0, sizeof(measurement_t)); int screenshots = 0; bool first_screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!first_screenshot_successful) { *error = "Failed to read data from test pattern."; return false; } if (measurement.test_mode == TEST_MODE_NATIVE_REFERENCE) { uint8_t *test_pattern = (uint8_t *)malloc(pattern_bytes); memset(test_pattern, 0, pattern_bytes); for (int i = 0; i < pattern_magic_bytes; i++) { test_pattern[i] = rand(); } if (!open_native_reference_window(test_pattern)) { *error = "Failed to open native reference window."; return false; } bool return_value = measure_latency(test_pattern, out_key_down_latency_ms, out_scroll_latency_ms, out_max_js_pause_time_ms, out_max_css_pause_time_ms, out_max_scroll_pause_time_ms, error); if (!close_native_reference_window()) { debug_log("Failed to close native reference window."); }; return return_value; } int64_t start_time = measurement.screenshot_time; previous_measurement = measurement; statistic javascript_frames; statistic css_frames; statistic key_down_events; statistic scroll_stats; init_statistic("javascript_frames", &javascript_frames, measurement.javascript_frames, start_time); init_statistic("key_down_events", &key_down_events, measurement.key_down_events, start_time); init_statistic("css_frames", &css_frames, measurement.css_frames, start_time); init_statistic("scroll", &scroll_stats, measurement.scroll_position, start_time); int sent_events = 0; int scroll_x = x + 40; int scroll_y = y + 40; int64_t last_scroll_sent = start_time; if (measurement.test_mode == TEST_MODE_SCROLL_LATENCY) { send_scroll_down(scroll_x, scroll_y); scroll_stats.previous_change_time = get_nanoseconds(); } while(true) { bool screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!screenshot_successful) { *error = "Test window moved during test. The test window must remain " "stationary and focused during the entire test."; return false; } if (measurement.test_mode == TEST_MODE_ABORT) { *error = "Test aborted."; return false; } screenshots++; int64_t screenshot_time = measurement.screenshot_time; int64_t previous_screenshot_time = previous_measurement.screenshot_time; debug_log("screenshot time %f", (screenshot_time - previous_screenshot_time) / (double)nanoseconds_per_millisecond); update_statistic(&javascript_frames, measurement.javascript_frames, screenshot_time, previous_screenshot_time); update_statistic(&key_down_events, measurement.key_down_events, screenshot_time, previous_screenshot_time); update_statistic(&css_frames, measurement.css_frames, screenshot_time, previous_screenshot_time); bool scroll_updated = update_statistic(&scroll_stats, measurement.scroll_position, screenshot_time, previous_screenshot_time); if (measurement.test_mode == TEST_MODE_JAVASCRIPT_LATENCY) { if (key_down_events.measurements >= latency_measurements_to_take) { break; } if (key_down_events.value_delta > sent_events) { *error = "More events received than sent! This is probably a bug in " "the test."; return false; } if (screenshot_time - key_down_events.previous_change_time > event_response_timeout_ms * nanoseconds_per_millisecond) { *error = "Browser did not respond to keyboard input. Make sure the " "test page remains focused for the entire test."; return false; } if (key_down_events.value_delta == sent_events) { // We want to avoid sending input events at a predictable time relative // to frames, so introduce a random delay of up to 1 frame (16.67 ms) // before sending the next event. usleep((rand() % 17) * 1000); if (!send_keystroke_z()) { *error = "Failed to send keystroke for \"Z\" key to test window."; return false; } key_down_events.previous_change_time = get_nanoseconds(); sent_events++; } } else if (measurement.test_mode == TEST_MODE_SCROLL_LATENCY) { if (scroll_stats.measurements >= latency_measurements_to_take) { break; } if (screenshot_time - scroll_stats.previous_change_time > event_response_timeout_ms * nanoseconds_per_millisecond) { *error = "Browser did not respond to scroll events. Make sure the " "test page remains focused for the entire test."; return false; } if (scroll_updated) { // We saw the start of a scroll. Wait for the scroll animation to // finish before continuing. We assume the animation is finished if // it's been 100 milliseconds since we last saw the scroll position // change. int64_t scroll_update_time = screenshot_time; int64_t scroll_wait_start_time = screenshot_time; while (screenshot_time - scroll_update_time < 100 * nanoseconds_per_millisecond) { screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!screenshot_successful) { *error = "Test window moved during test. The test window must " "remain stationary and focused during the entire test."; return false; } screenshot_time = measurement.screenshot_time; if (screenshot_time - scroll_wait_start_time > nanoseconds_per_second) { *error = "Browser kept scrolling for more than 1 second after a " "single scrollwheel event."; return false; } if (measurement.scroll_position != scroll_stats.value) { scroll_stats.value = measurement.scroll_position; scroll_update_time = screenshot_time; } } // We want to avoid sending input events at a predictable time // relative to frames, so introduce a random delay of up to 1 frame // (16.67 ms) before sending the next event. usleep((rand() % 17) * 1000); send_scroll_down(scroll_x, scroll_y); scroll_stats.previous_change_time = get_nanoseconds(); } } else if (measurement.test_mode == TEST_MODE_PAUSE_TIME) { // For the pause time test we want the browser to scroll continuously. // Send a scroll event every frame. if (screenshot_time - last_scroll_sent > 17 * nanoseconds_per_millisecond) { send_scroll_down(scroll_x, scroll_y); last_scroll_sent = get_nanoseconds(); } } else if (measurement.test_mode == TEST_MODE_PAUSE_TIME_TEST_FINISHED) { break; } else { *error = "Invalid test type. This is a bug in the test."; return false; } if (screenshot_time - start_time > test_timeout_ms * nanoseconds_per_millisecond) { *error = "Timeout."; return false; } previous_measurement = measurement; usleep(0); } // The latency we report is the midpoint of the interval given by the average // upper and lower bounds we've computed. *out_key_down_latency_ms = (upper_bound_ms(&key_down_events) + lower_bound_ms(&key_down_events)) / 2; *out_scroll_latency_ms = (upper_bound_ms(&scroll_stats) + lower_bound_ms(&scroll_stats) / 2); *out_max_js_pause_time_ms = javascript_frames.max_lower_bound / (double) nanoseconds_per_millisecond; *out_max_css_pause_time_ms = css_frames.max_lower_bound / (double) nanoseconds_per_millisecond; *out_max_scroll_pause_time_ms = scroll_stats.max_lower_bound / (double) nanoseconds_per_millisecond; debug_log("out_key_down_latency_ms: %f out_scroll_latency_ms: %f " "out_max_js_pause_time_ms: %f out_max_css_pause_time: %f\n " "out_max_scroll_pause_time_ms: %f", *out_key_down_latency_ms, *out_scroll_latency_ms, *out_max_js_pause_time_ms, *out_max_css_pause_time_ms, *out_max_scroll_pause_time_ms); return true; }