diff --git a/website/api/manager/ManagerAPI.php b/website/api/manager/ManagerAPI.php index e924ea911..c3403f597 100644 --- a/website/api/manager/ManagerAPI.php +++ b/website/api/manager/ManagerAPI.php @@ -65,6 +65,11 @@ private function getTrueskillMatchQuality($rankingValues) { return floatval($lines[0]); } + private function clearGameTask($gametaskID) { + $gametaskID = $this->mysqli->real_escape_string($gametaskID); + $this->insert("DELETE FROM GameTaskUser WHERE gametaskID={$gametaskID}"); + $this->insert("DELETE FROM GameTask WHERE gametaskID={$gametaskID}"); + } /////////////////////////API ENDPOINTS\\\\\\\\\\\\\\\\\\\\\\\\\\\\ @@ -88,7 +93,7 @@ protected function task() { $seedPlayer = null; $randValue = mt_rand() / mt_getrandmax(); if($randValue > 0.5) { - $seedPlayer = $this->select("SELECT * FROM User WHERE isRunning = 1 and numGames < 400 order by rand()*-pow(sigma, 2) LIMIT 1"); + $seedPlayer = $this->select("SELECT u.* FROM (SELECT MAX(p.timestamp) as maxTime, pu.userID as userID from GameTaskUser pu INNER JOIN GameTask p ON p.gametaskID = pu.gametaskID GROUP BY pu.userID) temptable RIGHT JOIN User u on u.userID = temptable.userID WHERE (maxTime IS NULL OR maxTime < DATE_SUB(NOW(), INTERVAL 10 MINUTE)) AND isRunning = 1 and numGames < 400 order by rand()*-pow(sigma, 2) LIMIT 1"); } if ($randValue > 0.25 && $randValue <= 0.5) { $seedPlayer = $this->select("SELECT * FROM (SELECT u.* FROM (SELECT MAX(g.timestamp) as maxTime, gu.userID as userID FROM GameUser gu INNER JOIN Game g ON g.gameID=gu.gameID GROUP BY gu.userID) temptable INNER JOIN User u on u.userID = temptable.userID where numGames < 400 and isRunning = 1 order by maxTime ASC limit 15) orderedTable order by rand() limit 1;"); @@ -105,10 +110,23 @@ protected function task() { $sizes = array(20, 25, 25, 30, 30, 30, 35, 35, 35, 35, 40, 40, 40, 45, 45, 50); $size = $sizes[array_rand($sizes)]; + // Record pairing + $worker = $this->select("SELECT * FROM Worker WHERE apiKey=".$this->mysqli->real_escape_string($this->apiKey)." LIMIT 1"); + $this->insert("INSERT INTO GameTask (workerID) VALUES (".$worker["workerID"].")"); + $gametaskID = $this->mysqli->insert_id; + $playerValues = array(); + foreach($players as $player) { + $playerValues[] = "(".$gametaskID.", ".$player["userID"].")"; + } + $playerValues = implode(",", $playerValues); + $playerInsert = "INSERT INTO GameTaskUser (gametaskID, userID) VALUES ".$playerValues; + $this->insert($playerInsert); + // Send game task if(count($players) == $numPlayers) { return array( "type" => "game", + "gametaskID" => $gametaskID, "width" => $size, "height" => $size, "users" => $players @@ -161,6 +179,9 @@ protected function game() { // Will need email credentials for email sending, numSubmissions for version checking, and mu + sigma so we can update trueskill $storedUser = $this->select("SELECT userID, onEmailList, email, numSubmissions, mu, sigma FROM User WHERE userID=".$this->mysqli->real_escape_string($user['userID'])); if(intval($storedUser['numSubmissions']) != intval($user['numSubmissions'])) { + if(isset($_POST['gametaskID'])) { + $this->clearGameTask($_POST['gametaskID']); + } return null; } $users[$key] = array_merge($user, $storedUser); // Second param overwrites first param @@ -261,6 +282,10 @@ protected function game() { } $query .= " ELSE rank END;"; $this->insert($query); + + if(isset($_POST['gametaskID'])) { + $this->clearGameTask($_POST['gametaskID']); + } } } diff --git a/website/sql/schema.sql b/website/sql/schema.sql index 1af52bb00..360335e23 100644 --- a/website/sql/schema.sql +++ b/website/sql/schema.sql @@ -54,6 +54,34 @@ CREATE TABLE `GameUser` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `GameTask` +-- + +DROP TABLE IF EXISTS `GameTask`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `GameTask` ( + `gametaskID` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `workerID` mediumint(8) unsigned NOT NULL, + `timestamp` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`gametaskID`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `GameTaskUser` +-- + +DROP TABLE IF EXISTS `GameTaskUser`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `GameTaskUser` ( + `gametaskID` mediumint(8) unsigned NOT NULL, + `userID` mediumint(8) unsigned NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `User` -- diff --git a/worker/backend.py b/worker/backend.py index f15e7d065..460fc240a 100644 --- a/worker/backend.py +++ b/worker/backend.py @@ -86,14 +86,14 @@ def compileResult(userID, didCompile, language, errors=None): r = requests.post(MANAGER_URL+"compile", data={"apiKey": API_KEY, "userID": userID, "didCompile": int(didCompile), "language": language, "errors": errors}) print("Posting compile result %s\n" % r.text) -def gameResult(width, height, users, replayPath, errorPaths): +def gameResult(gametaskID, width, height, users, replayPath, errorPaths): """Posts the result of a game task""" print("Posting game result %s (GMT)\n" % str(strftime("%Y-%m-%d %H:%M:%S", gmtime()))) files = {os.path.basename(replayPath): open(replayPath, "rb").read()} for path in errorPaths: files[os.path.basename(path)] = open(path, "rb").read() - r = requests.post(MANAGER_URL+"game", data={"apiKey": API_KEY, "mapWidth": str(width), "mapHeight": str(height), "users": json.dumps(users)}, files=files) + r = requests.post(MANAGER_URL+"game", data={"apiKey": API_KEY, "gametaskID": gametaskID, "mapWidth": str(width), "mapHeight": str(height), "users": json.dumps(users)}, files=files) print("Got game result %s (GMT)\n" % str(strftime("%Y-%m-%d %H:%M:%S", gmtime()))) print("\n-------Game result:-----") print(r.text) - print("------------------------\n") + print("------------------------\n") \ No newline at end of file diff --git a/worker/worker.py b/worker/worker.py index c7b6f8e88..7eb9ca3cd 100644 --- a/worker/worker.py +++ b/worker/worker.py @@ -142,8 +142,11 @@ def parseGameOutput(output, users): return width, height, users, replayPath, errorPaths -def executeGameTask(width, height, users, backend): +def executeGameTask(gameTask, backend): """Downloads compiled bots, runs a game, and posts the results of the game""" + width = int(gameTask["width"]) + height = int(gameTask["height"]) + users = gameTask["users"] print("Running game with width %d, height %d\n" % (width, height)) print("Users objects %s\n" % (str(users))) @@ -157,7 +160,7 @@ def executeGameTask(width, height, users, backend): fIn.close() fOut.close() - backend.gameResult(width, height, users, replayArchivePath, errorPaths) + backend.gameResult(gameTask["gametaskID"], width, height, users, replayArchivePath, errorPaths) filelist = glob.glob("*.log") for f in filelist: os.remove(f) @@ -165,7 +168,7 @@ def executeGameTask(width, height, users, backend): os.remove(replayPath) os.remove(replayArchivePath) -if __name__ == "__main__": +def main(): print("\n\n\n\nStarting up worker...\n\n\n") while True: try: @@ -179,7 +182,7 @@ def executeGameTask(width, height, users, backend): executeCompileTask(task["user"], backend) else: print("Running a game task...\n") - executeGameTask(int(task["width"]), int(task["height"]), task["users"], backend) + executeGameTask(task, backend) else: print("No task available at time %s (GMT). Sleeping...\n" % str(strftime("%Y-%m-%d %H:%M:%S", gmtime()))) sleep(2) @@ -188,3 +191,5 @@ def executeGameTask(width, height, users, backend): print("Sleeping...\n") sleep(2) +if __name__ == "__main__": + main()