From 7676ede280c2f72fbcbe4a93c4d56eddce9bbcea Mon Sep 17 00:00:00 2001 From: ProjectInfinity Date: Thu, 23 Nov 2017 21:01:11 +0100 Subject: [PATCH] Add VRC support. --- .gitignore | 3 +- plugin.yml | 2 +- src/ProjectInfinity/PocketVote/PocketVote.php | 20 +++ .../PocketVote/VoteListener.php | 1 + .../PocketVote/task/VRCCheckTask.php | 159 ++++++++++++++++++ .../PocketVote/util/VoteManager.php | 28 +++ 6 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 src/ProjectInfinity/PocketVote/task/VRCCheckTask.php diff --git a/.gitignore b/.gitignore index 2c8c693..96b157a 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ crashlytics-build.properties fabric.properties cacert.pem -/config.yml \ No newline at end of file +/config.yml +vrc/ diff --git a/plugin.yml b/plugin.yml index fd09403..f2a78a6 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: PocketVote main: ProjectInfinity\PocketVote\PocketVote -version: 2.0.3 +version: 2.1.0 api: [3.0.0-ALPHA6, 3.0.0-ALPHA7, 3.0.0-ALPHA8, 3.0.0-ALPHA9] author: ProjectInfinity permissions: diff --git a/src/ProjectInfinity/PocketVote/PocketVote.php b/src/ProjectInfinity/PocketVote/PocketVote.php index 4063936..cd81577 100644 --- a/src/ProjectInfinity/PocketVote/PocketVote.php +++ b/src/ProjectInfinity/PocketVote/PocketVote.php @@ -35,6 +35,7 @@ class PocketVote extends PluginBase { public static $cert; public static $dev; + public static $hasVRC; /** @var VoteManager $voteManager */ private $voteManager; @@ -128,6 +129,25 @@ public function onEnable() { $this->getServer()->getPluginManager()->registerEvents(new VoteListener($this), $this); + # Check if the VRC folder exists for legacy voting sites. + if(file_exists($this->getDataFolder().'vrc')) { + $this->getLogger()->info(TextFormat::GOLD.'VRC folder found, to enable sites that support VRC files. Place them in the plugins/PocketVote/vrc folder and ensure the file name ends with .vrc'); + $vrcFiles = glob($this->getDataFolder().'vrc/*.{vrc}', GLOB_BRACE); + foreach($vrcFiles as $file) { + $fh = fopen($file, 'rb'); + $raw = fread($fh, filesize($file)); + fclose($fh); + $data = json_decode($raw); + if(!$raw || !$data) { + $this->getLogger()->warning(TextFormat::RED.'VRC file '.$file.' could not be read.'); + continue; + } + $this->voteManager->addVRC((object)['website' => $data->website, 'check' => $data->check, 'claim' => $data->claim]); + self::$hasVRC = true; + $this->getLogger()->info(TextFormat::GREEN.'VRC enabled for '.$data->website); + } + } + $this->schedulerTask = $this->getServer()->getScheduler()->scheduleRepeatingTask(new SchedulerTask($this), 1200); # 1200 ticks = 60 seconds. # Get voting link. $this->getServer()->getScheduler()->scheduleAsyncTask(new VoteLinkTask($this->identity)); diff --git a/src/ProjectInfinity/PocketVote/VoteListener.php b/src/ProjectInfinity/PocketVote/VoteListener.php index 26f8217..132a622 100644 --- a/src/ProjectInfinity/PocketVote/VoteListener.php +++ b/src/ProjectInfinity/PocketVote/VoteListener.php @@ -51,6 +51,7 @@ public function onVoteEvent(VoteEvent $event) { * @priority LOWEST */ public function onPlayerJoin(PlayerJoinEvent $event) { + if(PocketVote::$hasVRC) $this->vm->scheduleVRCTask($event->getPlayer()->getName()); # TODO: This should be possible to disable and only allow through commands. if(!$this->vm->hasVotes($event->getPlayer()->getName())) return; $sender = new ConsoleCommandSender(); diff --git a/src/ProjectInfinity/PocketVote/task/VRCCheckTask.php b/src/ProjectInfinity/PocketVote/task/VRCCheckTask.php new file mode 100644 index 0000000..3f88d71 --- /dev/null +++ b/src/ProjectInfinity/PocketVote/task/VRCCheckTask.php @@ -0,0 +1,159 @@ +player = $player; + $this->vrcs = PocketVote::getPlugin()->getVoteManager()->getVRC(); + $this->cert = PocketVote::$cert; + } + + public function onRun() { + $results = []; + foreach($this->vrcs as $vrc) { + $curl = curl_init(str_replace('{USERNAME}', $this->player, $vrc->check)); + + $url = parse_url($vrc->check); + + curl_setopt_array($curl, [ + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_PORT => $url['scheme'] === 'https' ? 443 : 80, + CURLOPT_HEADER => false, + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_SSL_VERIFYHOST => 2, + CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, + CURLOPT_CAINFO => $this->cert + ]); + + $res = curl_exec($curl); + + if($res === false) { + $results[] = $this->createResult(true, null, curl_error($curl)); + curl_close($curl); + } else { + + $result = json_decode($res); + + if(!isset($result->voted) || !isset($result->claimed)) { + $results[] = $this->createResult(true, ['message' => 'Vote or claim field was missing in response from '.$url['host']]); + $this->setResult($results); + curl_close($curl); + return; + } + + # There is a vote to claim. + if($result->voted && !$result->claimed) { + curl_close($curl); + + $curl = curl_init(str_replace('{USERNAME}', $this->player, $vrc->claim)); + + $url = parse_url($vrc->check); + + curl_setopt_array($curl, [ + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_PORT => $url['scheme'] === 'https' ? 443 : 80, + CURLOPT_HEADER => false, + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_SSL_VERIFYHOST => 2, + CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, + CURLOPT_CAINFO => $this->cert + ]); + + $res = curl_exec($curl); + + if($res === false) { + $results[] = $this->createResult(true, null, curl_error($curl)); + curl_close($curl); + } else { + + $result = json_decode($res); + curl_close($curl); + + if(!isset($result->voted) || !isset($result->claimed)) { + $results[] = $this->createResult(true, ['message' => 'Vote or claim field was missing in response from '.$url['host']]); + $this->setResult($results); + return; + } + + # Claim failed. + if($result->voted && !$result->claimed) { + $results[] = $this->createResult(true, ['message' => 'Attempted to claim a vote but it failed. Site: '.$url['host']]); + $this->setResult($results); + return; + } + + # Vote claim succeeded! + $results[] = $this->createResult(false, json_decode(json_encode([ + 'success' => true, + 'payload' => [ + 'player' => $this->player, + 'ip' => 'unknown', + 'site' => $url['host'] + ] + ]))); + + } + } + + } + } + $this->setResult($results); + } + + private function createResult($error, $res, $customError = null) { + $r = new TaskResult(); + $r->setError($error); + if($error) { + if(!isset($customError)) { + $r->setErrorData(['message' => $res->message]); + } + else { + $r->setErrorData(['message' => $customError]); + } + } + # Had votes. + if(isset($res->payload) && $res->success) $r->setVotes($res->payload); + + return $r; + } + + public function onCompletion(Server $server) { + if(!$this->hasResult()) { + $server->getLogger()->emergency('A VRC task finished without a result. This should never happen.'); + return; + } + + /** @var TaskResult[] $results */ + $results = $this->getResult(); + + if(!is_array($results)) { + $server->getLogger()->warning('VRCCheckTask result was not an array. This is a problem...'); + return; + } + + foreach($results as $result) { + $vote = (object) $result->getVotes(); + $server->getPluginManager()->callEvent( + new VoteEvent(PocketVote::getPlugin(), + $vote->player, + $vote->ip, + $vote->site + ) + ); + } + + # Removes task from the array that prevents duplicate tasks. + PocketVote::getPlugin()->getVoteManager()->removeVRCTask($this->player); + } +} \ No newline at end of file diff --git a/src/ProjectInfinity/PocketVote/util/VoteManager.php b/src/ProjectInfinity/PocketVote/util/VoteManager.php index bd928b5..2b78cd7 100644 --- a/src/ProjectInfinity/PocketVote/util/VoteManager.php +++ b/src/ProjectInfinity/PocketVote/util/VoteManager.php @@ -4,6 +4,7 @@ use ProjectInfinity\PocketVote\PocketVote; use ProjectInfinity\PocketVote\task\ExpireVotesTask; +use ProjectInfinity\PocketVote\task\VRCCheckTask; class VoteManager { @@ -11,15 +12,42 @@ class VoteManager { /** @var $votes array */ private $votes; + /** @var $loadedVRC array */ + private $loadedVRC; + /** @var $currentVRCTasks array */ + private $currentVRCTasks; private $voteLink = null; public function __construct(PocketVote $plugin) { $this->plugin = $plugin; $this->votes = $plugin->getConfig()->get('votes', []); + $this->loadedVRC = []; + $this->currentVRCTasks = []; if($this->plugin->expiration > 0) $plugin->getServer()->getScheduler()->scheduleDelayedRepeatingTask(new ExpireVotesTask(), 300, 6000); } + public function addVRC($record) { + $this->loadedVRC[] = $record; + } + + public function getVRC(): array { + return $this->loadedVRC; + } + + public function removeVRCTask($player) { + if(!isset($this->currentVRCTasks[$player])) return; + unset($this->currentVRCTasks[$player]); + } + + public function scheduleVRCTask($player) { + if((PocketVote::$hasVRC && !$this->plugin->multiserver) || (PocketVote::$hasVRC && $this->plugin->multiserver && $this->plugin->multiserver_role === 'master')) { + if(isset($this->currentVRCTasks[$player])) return; + # Only run when VRC is enabled and multiserver is off or VRC is enabled and multiserver and server role is set to master. + $this->plugin->getServer()->getScheduler()->scheduleAsyncTask(new VRCCheckTask($player)); + } + } + public function getVoteLink() { return $this->voteLink; }