From 4dd124ad7d6cd10b112a721805eeeccc368f0e93 Mon Sep 17 00:00:00 2001 From: Jason Tarka Date: Mon, 26 Mar 2018 11:06:25 -0400 Subject: [PATCH 1/4] Functional fixes for bases & links - Handle http/https/ftp/etc. prefix for base URLs - Previously `http://example.com` would lead to the system calling `http:12345`; now will call example.com:12345 - Better display of non-http(s) links - If there's a port listed, show the full `nc 1.2.3.4 567`, if not, show just `1.2.3.4` (previously would show `nc 1.2.3.4 undefined) - Readonly, rather than disabled, so the text can be scrolled & selected --- src/models/Level.php | 3 ++- src/static/js/fb-ctf.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/models/Level.php b/src/models/Level.php index e559621b..855081f6 100644 --- a/src/models/Level.php +++ b/src/models/Level.php @@ -1232,7 +1232,8 @@ private static function levelFromRow(Map $row): Level { public static async function genBaseIP(int $base_id): Awaitable { $links = await Link::genAllLinks($base_id); $link = $links[0]; - $ip = explode(':', $link->getLink())[0]; + $ip = preg_replace('$[a-zA-Z]+://$', '', $link->getLink()); + $ip = explode(':', $ip)[0]; return $ip; } diff --git a/src/static/js/fb-ctf.js b/src/static/js/fb-ctf.js index a59058cc..b08e858f 100644 --- a/src/static/js/fb-ctf.js +++ b/src/static/js/fb-ctf.js @@ -939,7 +939,8 @@ function setupInputListeners() { } else { var ip = this.split(':')[0]; var port = this.split(':')[1]; - link = $('').attr('type', 'text').attr('disabled', true).attr('value', 'nc ' + ip + ' ' + port); + var value = port ? 'nc ' + ip + ' ' + port : ip; + link = $('').attr('type', 'text').attr('readonly', true).attr('value', value); } $('.capture-links', $container).append(link); $('.capture-links', $container).append($('
')); From b6dc76b9fc18695cd955ff6f251ffaec89117aa9 Mon Sep 17 00:00:00 2001 From: Jason Tarka Date: Mon, 26 Mar 2018 16:42:49 -0400 Subject: [PATCH 2/4] Reverse points/bonus for bases It makes more logical sense to have the capture score be the main capture points, and the holding score be the bonus points, rather than the previous capture = bonus, hold = points. - Adding version detection to allow old exports to be imported easily. - Extra logging to make debugging easier/possible - This will make upcoming changes to the base display easier. --- src/controllers/AdminController.php | 4 +-- src/models/Level.php | 39 +++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/controllers/AdminController.php b/src/controllers/AdminController.php index d8c002be..ae3277dd 100644 --- a/src/controllers/AdminController.php +++ b/src/controllers/AdminController.php @@ -2540,13 +2540,13 @@ class=
- +
- +
diff --git a/src/models/Level.php b/src/models/Level.php index 855081f6..7c3c19ba 100644 --- a/src/models/Level.php +++ b/src/models/Level.php @@ -148,9 +148,11 @@ private static function levelFromRow(Map $row): Level { public static async function importAll( array> $elements, ): Awaitable { + Utils::logMessage('Beginning import of '.count($elements).' levels'); foreach ($elements as $level) { $title = must_have_string($level, 'title'); $type = must_have_string($level, 'type'); + Utils::logMessage("> Importing $type level: $title"); $entity_iso_code = must_have_string($level, 'entity_iso_code'); $c = must_have_string($level, 'category'); $exist = await self::genAlreadyExist($type, $title, $entity_iso_code); @@ -158,11 +160,21 @@ private static function levelFromRow(Map $row): Level { Country::genCheckExists($entity_iso_code), Category::genCheckExists($c), ); // TODO: Combine Awaits - if (!$exist && $entity_exist && $category_exist) { + if ($exist) { + Utils::logMessage('>> Level already exists'); + } else if (!$entity_exist) { + Utils::logMessage(">> Country ($entity_iso_code) does not exist"); + } else if (!$category_exist) { + Utils::logMessage(">> Category ($c) does not exist"); + } else { list($entity, $category) = await \HH\Asio\va( Country::genCountry($entity_iso_code), Category::genSingleCategoryByName($c), ); // TODO: Combine Awaits + + // Handle previous versions + $level = self::correctForVersion($level); + $level_id = await self::genCreate( $type, $title, @@ -178,6 +190,7 @@ private static function levelFromRow(Map $row): Level { must_have_int($level, 'penalty'), ); if (array_key_exists('links', $level)) { + Utils::logMessage('>> Importing links'); $links = must_have_idx($level, 'links'); invariant(is_array($links), 'links must be of type array'); foreach ($links as $link) { @@ -185,6 +198,7 @@ private static function levelFromRow(Map $row): Level { } } if (array_key_exists('attachments', $level)) { + Utils::logMessage('>> Importing attachments'); $attachments = must_have_idx($level, 'attachments'); invariant( is_array($attachments), @@ -198,11 +212,29 @@ private static function levelFromRow(Map $row): Level { ); // TODO: Combine Awaits } } + Utils::logMessage('>> Success'); } } return true; } + private static function correctForVersion( + array $level + ): array { + $version = (isset($level['version']) && !is_null($level['version'])) ? $level['version'] : 1; + // base versions < 2.0 had bonus & points reversed + if ($level['type'] === 'base' && $version < 2) { + Utils::logMessage('>> Base version is < 2. Correcting data.'); + $capture_points = $level['bonus']; + $hold_points = $level['points']; + $level['points'] = $capture_points; + $level['bonus'] = $hold_points; + $level['bonus_fix'] = $hold_points; + } + + return $level; + } + // Export levels. public static async function exportAll( ): Awaitable>> { @@ -244,6 +276,7 @@ private static function levelFromRow(Map $row): Level { 'penalty' => $level->getPenalty(), 'links' => $link_array, 'attachments' => $attachment_array, + 'version' => 2.0 ); array_push($all_levels_data, $one_level); } @@ -1140,8 +1173,10 @@ private static function levelFromRow(Map $row): Level { $score = await ScoreLog::genAllPreviousScore($level_id, $team_id, false); if ($score) { - $points = $level->getPoints(); + // If the team has already captured this base, only give the bonus + $points = $level->getBonus(); } else { + // Otherwise give capture points $points = $level->getPoints() + $level->getBonus(); } From d6400129059a1f7e7669dd2ab54661a167c0793c Mon Sep 17 00:00:00 2001 From: Jason Tarka Date: Mon, 26 Mar 2018 17:17:52 -0400 Subject: [PATCH 3/4] Mark bases as "held" when the team doesn't change - Mark as 'held' rather than 'captured' when the controlling team hasn't changed - Don't spam the gameboard's activity log with messages of a team holding a base - Show in the activity log when a base is captured by another team --- src/inc/gameboard/modules/activity.php | 2 +- src/language/lang_en.php | 2 ++ src/models/ActivityLog.php | 25 +++++++++++++++-- src/models/Level.php | 2 +- src/models/ScoreLog.php | 39 ++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/inc/gameboard/modules/activity.php b/src/inc/gameboard/modules/activity.php index 1564cfa9..9d92ab69 100644 --- a/src/inc/gameboard/modules/activity.php +++ b/src/inc/gameboard/modules/activity.php @@ -13,7 +13,7 @@ class ActivityModuleController extends ModuleController { $activity_ul =
    ; list($all_activity, $config) = await \HH\Asio\va( - ActivityLog::genAllActivity(), + ActivityLog::genAllActivity(array('held')), Configuration::gen('language'), ); $language = $config->getValue(); diff --git a/src/language/lang_en.php b/src/language/lang_en.php index e5644cef..4c1f8825 100644 --- a/src/language/lang_en.php +++ b/src/language/lang_en.php @@ -516,6 +516,8 @@ //Translations for inc/* and inc/gameboard/* 'captured' => 'captured', + 'held' => + 'held', 'Status' => 'Status', 'Completed' => diff --git a/src/models/ActivityLog.php b/src/models/ActivityLog.php index 2cbcc7a3..8605ebf5 100644 --- a/src/models/ActivityLog.php +++ b/src/models/ActivityLog.php @@ -195,6 +195,20 @@ private static function activitylogFromRow( ); } + public static async function genHoldLog( + int $team_id, + int $level_id, + ): Awaitable { + $country_id = await Level::genCountryIdForLevel($level_id); + await self::genCreateActionLog( + "Team", + $team_id, + "held", + "Country", + $country_id, + ); + } + public static async function genCreateActionLog( string $subject_class, int $subject_id, @@ -278,14 +292,21 @@ private static function activitylogFromRow( } public static async function genAllActivity( + array $exclude_actions = array(''), bool $refresh = false, ): Awaitable> { $mc_result = self::getMCRecords('ALL_ACTIVITY'); if (!$mc_result || count($mc_result) === 0 || $refresh) { + $excludes = new Vector(); + foreach ($exclude_actions as $exclude) { + $excludes->add($exclude); + } + $db = await self::genDb(); $activity_log_lines = array(); - $result = await $db->query( - 'SELECT * FROM activity_log ORDER BY ts DESC LIMIT 100', + $result = await $db->queryf( + 'SELECT * FROM activity_log WHERE action NOT IN (%Ls) ORDER BY ts DESC LIMIT 100', + $excludes ); foreach ($result->mapRows() as $row) { $activity_log = self::activitylogFromRow($row); diff --git a/src/models/Level.php b/src/models/Level.php index 7c3c19ba..c5c3bf44 100644 --- a/src/models/Level.php +++ b/src/models/Level.php @@ -1188,7 +1188,7 @@ private static function correctForVersion( ); // Log the score... - await ScoreLog::genLogValidScore( + await ScoreLog::genLogBaseScore( $level_id, $team_id, $points, diff --git a/src/models/ScoreLog.php b/src/models/ScoreLog.php index d4813900..7baf0b6e 100644 --- a/src/models/ScoreLog.php +++ b/src/models/ScoreLog.php @@ -393,6 +393,45 @@ private static function scorelogFromRow(Map $row): ScoreLog { return $captured; } + // Log successful base capture or hold score. + public static async function genLogBaseScore( + int $level_id, + int $team_id, + int $points, + ): Awaitable { + $completed_level = await MultiTeam::genCompletedLevel($level_id); + Utils::logMessage( "Teams who have completed level ($level_id): " . var_export($completed_level, true)); + $captured = empty($completed_level) || end($completed_level)->getId() != $team_id; + + $db = await self::genDb(); + $result = + await $db->queryf( + 'INSERT INTO scores_log (ts, level_id, team_id, points, type) SELECT NOW(), %d, %d, %d, %s FROM DUAL', + $level_id, + $team_id, + $points, + 'base', + ); + + if ($captured === true) { + await ActivityLog::genCaptureLog($team_id, $level_id); + } else { + await ActivityLog::genHoldLog($team_id, $level_id); + } + self::invalidateMCRecords(); // Invalidate Memcached ScoreLog data. + ActivityLog::invalidateMCRecords('ALL_ACTIVITY'); // Invalidate Memcached ActivityLog data. + MultiTeam::invalidateMCRecords('ALL_TEAMS'); // Invalidate Memcached MultiTeam data. + MultiTeam::invalidateMCRecords('POINTS_BY_TYPE'); // Invalidate Memcached MultiTeam data. + MultiTeam::invalidateMCRecords('LEADERBOARD'); // Invalidate Memcached MultiTeam data. + $completed_level = await MultiTeam::genCompletedLevel($level_id); + if (count($completed_level) === 0) { + MultiTeam::invalidateMCRecords('TEAMS_FIRST_CAP'); // Invalidate Memcached MultiTeam data. + } + MultiTeam::invalidateMCRecords('TEAMS_BY_LEVEL'); // Invalidate Memcached MultiTeam data. + + return $captured; + } + public static async function genScoreLogUpdate( int $level_id, int $team_id, From 1f2428f5057f76d4e34f3166362c0bebd255fc02 Mon Sep 17 00:00:00 2001 From: Jason Tarka Date: Tue, 27 Mar 2018 11:24:28 -0400 Subject: [PATCH 4/4] Improve appearance of bases & links - Show points for both capturing & holding a base - Show non-http(s) links as full-width, so they're easier to read - Show "Capture Points" & "Keep points" when editing bases instead of "Points" & "Bonus" --- src/controllers/AdminController.php | 4 ++-- .../modals/CountryModalController.php | 12 ++++++++++++ src/language/lang_en.php | 4 ++++ src/static/css/scss/_modals.scss | 16 ++++++++++++++++ src/static/css/scss/_typography.scss | 14 +++++++++++--- src/static/js/fb-ctf.js | 11 ++++++++--- 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/controllers/AdminController.php b/src/controllers/AdminController.php index ae3277dd..3adae915 100644 --- a/src/controllers/AdminController.php +++ b/src/controllers/AdminController.php @@ -2894,7 +2894,7 @@ class=
    - +
    - + {tr('PTS')} + {tr('Capture')} +
    +
    + + {tr('PTS')} + {tr('Hold')}
    @@ -241,6 +247,12 @@ class=
    {tr('PTS')} + {tr('Capture')} +
    +
    + + {tr('PTS')} + {tr('Hold')}
    diff --git a/src/language/lang_en.php b/src/language/lang_en.php index 4c1f8825..7f2340f1 100644 --- a/src/language/lang_en.php +++ b/src/language/lang_en.php @@ -602,6 +602,10 @@ 'INACTIVE', 'PTS' => 'PTS', + 'Capture' => + 'Capture', + 'Hold' => + 'Hold', 'category' => 'category', 'capture_' => diff --git a/src/static/css/scss/_modals.scss b/src/static/css/scss/_modals.scss index a5e69092..71ea2ca2 100644 --- a/src/static/css/scss/_modals.scss +++ b/src/static/css/scss/_modals.scss @@ -164,6 +164,22 @@ } } +/** + * --country modal + */ +.modal--country-capture { + // link display + .capture-links { + @include flexbox; + flex-wrap: wrap; + + & > a, + & > input { + @include flex(1 0 100%); + } + } +} + /** * --tutorial */ diff --git a/src/static/css/scss/_typography.scss b/src/static/css/scss/_typography.scss index 89448df5..d2c6a899 100644 --- a/src/static/css/scss/_typography.scss +++ b/src/static/css/scss/_typography.scss @@ -382,7 +382,8 @@ table { /** * --point circles */ -.points-display { +.points-display, +.bonus-display { @include flexbox; @include align-center; @@ -397,18 +398,25 @@ table { width: 80px; height: 80px; - .points-number { + .points-number, + .bonus-number { font-size: 3em; display: block; line-height: 1; } - .points-label { + .points-label, + .bonus-label { font-size: .8em; text-transform: uppercase; } } +.bonus-display { + margin-left: 10px; + margin-right: -10px; +} + /* -------------------------------------------- * inactive country * -------------------------------------------- */ diff --git a/src/static/js/fb-ctf.js b/src/static/js/fb-ctf.js index b08e858f..959ed7c7 100644 --- a/src/static/js/fb-ctf.js +++ b/src/static/js/fb-ctf.js @@ -909,6 +909,7 @@ function setupInputListeners() { hint = data ? data.hint : '', hint_cost = data ? data.hint_cost : -1, points = data ? data.points : '', + bonus = data ? data.bonus : '', category = data ? data.category : '', type = data ? data.type : '', completed = data ? data.completed : '', @@ -943,11 +944,11 @@ function setupInputListeners() { link = $('').attr('type', 'text').attr('readonly', true).attr('value', value); } $('.capture-links', $container).append(link); - $('.capture-links', $container).append($('
    ')); link_c++; }); } $('.points-number', $container).text(points); + $('.bonus-number', $container).text(bonus); $('.country-type', $container).text(type); $('.country-category', $container).text(category); $('.country-owner', $container).text(owner); @@ -959,9 +960,11 @@ function setupInputListeners() { }); } - // Hide flag submission for bases + // Hide flag submission for bases & display base-style points + console.log('Country type:', type); if (type === 'base') { - $('.answer_no_bases').addClass('completely-hidden'); + $('.answer_no_bases, .points-label, .bonus-label').addClass('completely-hidden'); + $('.bonus-display, .base-bonus, .base-points').removeClass('completely-hidden'); } // Hide flag submission for captured levels @@ -1106,6 +1109,7 @@ function setupInputListeners() { top: mouse_y + 'px' }), points = data ? data.points : '', + bonus = data ? data.bonus : '', category = data ? data.category : '', title = data ? data.title : '', type = data ? data.type : ''; @@ -1113,6 +1117,7 @@ function setupInputListeners() { $('.country-name', $container).text(country); $('.country-title', $container).text(title); $('.points-number', $container).text(points); + $('.bonus-number', $container).text(bonus); $('.country-type', $container).text(type); $('.country-category', $container).text(category); });