-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
name: DiscordVerify | ||
author: alvin0319 | ||
main: TeamBixby\DiscordVerify\DiscordVerify | ||
version: 1.0.0 | ||
api: 3.0.0 | ||
|
||
permissions: | ||
discordverify.command.use: | ||
default: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
address: ~ | ||
port: ~ | ||
password: ~ | ||
|
||
bindPort: ~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TeamBixby\DiscordVerify; | ||
|
||
use ClassLoader; | ||
use pocketmine\utils\MainLogger; | ||
use TeamBixby\DiscordVerify\util\DiscordException; | ||
use Thread; | ||
use Volatile; | ||
use function get_class; | ||
use function gettype; | ||
use function is_array; | ||
use function is_object; | ||
use function is_string; | ||
use function json_decode; | ||
use function json_encode; | ||
use function socket_bind; | ||
use function socket_close; | ||
use function socket_create; | ||
use function socket_last_error; | ||
use function socket_recvfrom; | ||
use function socket_sendto; | ||
use function socket_set_nonblock; | ||
use function socket_strerror; | ||
use function trim; | ||
use const PHP_INT_MAX; | ||
use const SOCKET_EWOULDBLOCK; | ||
|
||
class DiscordThread extends Thread{ | ||
|
||
public const KEY_ACTION = "action"; | ||
|
||
public const KEY_DATA = "data"; | ||
|
||
protected ClassLoader $classLoader; | ||
|
||
protected string $targetHost; | ||
|
||
protected int $targetPort; | ||
|
||
protected int $bindPort; | ||
|
||
protected string $password; | ||
|
||
protected Volatile $inboundQueue; | ||
|
||
protected Volatile $outboundQueue; | ||
|
||
protected bool $shutdown = true; | ||
|
||
public function __construct(ClassLoader $loader, string $targetHost, int $targetPort, int $bindPort, string $password, Volatile $inboundQueue, Volatile $outboundQueue){ | ||
$this->classLoader = $loader; | ||
$this->targetHost = $targetHost; | ||
$this->targetPort = $targetPort; | ||
$this->bindPort = $bindPort; | ||
$this->password = $password; | ||
$this->inboundQueue = $inboundQueue; | ||
$this->outboundQueue = $outboundQueue; | ||
} | ||
|
||
/** | ||
* @throws DiscordException | ||
*/ | ||
public function run() : void{ | ||
$this->classLoader->register(); | ||
|
||
$this->shutdown = false; | ||
|
||
$sendSock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); | ||
$recvSock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); | ||
|
||
socket_set_nonblock($recvSock); | ||
|
||
if($sendSock === false || $recvSock === false){ | ||
throw DiscordException::wrap("Failed to create socket"); | ||
} | ||
|
||
if(@socket_bind($recvSock, "0.0.0.0", $this->bindPort) === false){ | ||
throw DiscordException::wrap("Failed to bind port"); | ||
} | ||
|
||
socket_set_nonblock($recvSock); | ||
|
||
while(!$this->shutdown){ | ||
$this->receiveData($recvSock); | ||
$this->sendData($sendSock); | ||
} | ||
socket_close($recvSock); | ||
socket_close($sendSock); | ||
} | ||
|
||
/** | ||
* @param resource $recvSock | ||
* | ||
* @throws DiscordException | ||
*/ | ||
private function receiveData($recvSock) : void{ | ||
$buffer = ""; | ||
if(@socket_recvfrom($recvSock, $buffer, 65535, 0, $source, $port) === false){ | ||
$errno = socket_last_error($recvSock); | ||
if($errno === SOCKET_EWOULDBLOCK){ | ||
return; | ||
} | ||
throw new DiscordException("Failed to recv (errno $errno): " . trim(socket_strerror($errno)), $errno); | ||
//return; | ||
} | ||
if($buffer !== null && $buffer !== ""){ | ||
$data = json_decode($buffer, true); | ||
if(!is_array($data)){ | ||
throw DiscordException::wrap("Expected array, got " . (is_object($data) ? get_class($data) : gettype($data))); | ||
} | ||
$password = $data["password"] ?? null; | ||
if(!is_string($password)){ | ||
MainLogger::getLogger()->notice("Bad socket received from $source:$port"); | ||
MainLogger::getLogger()->notice("Password does not exist on response body"); | ||
return; | ||
} | ||
if($password !== $this->password){ | ||
MainLogger::getLogger()->notice("Bad socket received from $source:$port"); | ||
MainLogger::getLogger()->notice("Password does not match"); | ||
return; | ||
} | ||
$action = $data["action"]; | ||
$actionData = $data["data"]; | ||
|
||
$this->outboundQueue[] = [ | ||
self::KEY_ACTION => $action, | ||
self::KEY_DATA => $actionData | ||
]; | ||
} | ||
} | ||
|
||
/** | ||
* @param resource $sendSock | ||
* | ||
* @throws DiscordException | ||
*/ | ||
private function sendData($sendSock) : void{ | ||
while($this->inboundQueue->count() > 0){ | ||
$chunk = $this->inboundQueue->shift(); | ||
$data = [ | ||
"data" => $chunk, | ||
"password" => $this->password | ||
]; | ||
if(@socket_sendto($sendSock, json_encode($data), PHP_INT_MAX, 0, $this->targetHost, $this->targetPort) === false){ | ||
$errno = socket_last_error($sendSock); | ||
if($errno === SOCKET_EWOULDBLOCK){ | ||
return; | ||
} | ||
throw DiscordException::wrap("Failed to send socket: " . trim(socket_strerror($errno))); | ||
} | ||
} | ||
} | ||
|
||
public function shutdown() : void{ | ||
$this->shutdown = true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TeamBixby\DiscordVerify; | ||
|
||
use pocketmine\plugin\PluginBase; | ||
use pocketmine\scheduler\ClosureTask; | ||
use pocketmine\utils\SingletonTrait; | ||
use pocketmine\utils\TextFormat; | ||
use TeamBixby\DiscordVerify\command\VerifyCommand; | ||
use TeamBixby\DiscordVerify\util\DiscordException; | ||
use Volatile; | ||
use function file_exists; | ||
use function file_get_contents; | ||
use function file_put_contents; | ||
use function json_decode; | ||
use function json_encode; | ||
use function strtolower; | ||
use const PTHREADS_INHERIT_ALL; | ||
|
||
final class DiscordVerify extends PluginBase{ | ||
use SingletonTrait; | ||
|
||
public const ACTION_VERIFIED = "verified"; | ||
public const ACTION_UNVERIFIED = "unverified"; | ||
|
||
protected Volatile $inboundQueue; | ||
|
||
protected Volatile $outboundQueue; | ||
|
||
protected DiscordThread $thread; | ||
|
||
protected array $data = []; | ||
|
||
protected bool $canThreadClose = false; | ||
|
||
public function onLoad() : void{ | ||
self::setInstance($this); | ||
} | ||
|
||
public function onEnable() : void{ | ||
$this->saveDefaultConfig(); | ||
$config = $this->getConfig(); | ||
|
||
if($config->get("address", null) === null || $config->get("port", null) === null || $config->get("password", null) === null || $config->get("bindPort", null) === null){ | ||
$this->getLogger()->emergency("Please fill the config correctly before use this plugin!"); | ||
$this->getServer()->getPluginManager()->disablePlugin($this); | ||
return; | ||
} | ||
|
||
$this->inboundQueue = new Volatile(); | ||
$this->outboundQueue = new Volatile(); | ||
|
||
try{ | ||
$this->thread = new DiscordThread($this->getServer()->getLoader(), $config->get("address"), $config->get("port"), $config->get("bindPort"), $config->get("password"), $this->inboundQueue, $this->outboundQueue); | ||
$this->thread->start(PTHREADS_INHERIT_ALL); | ||
$this->canThreadClose = true; | ||
}catch(DiscordException $e){ | ||
$this->getLogger()->emergency("Failed to run DiscordThread"); | ||
$this->getLogger()->logException($e); | ||
$this->canThreadClose = false; | ||
$this->getServer()->getPluginManager()->disablePlugin($this); | ||
return; | ||
} | ||
|
||
if(file_exists($file = $this->getDataFolder() . "verified_players.json")){ | ||
$this->data = json_decode(file_get_contents($file), true); | ||
} | ||
|
||
$this->getScheduler()->scheduleRepeatingTask(new ClosureTask(function(int $unused) : void{ | ||
while($this->outboundQueue->count() > 0){ | ||
$chunk = $this->outboundQueue->shift(); | ||
$data = $chunk["data"]; | ||
switch($chunk["action"]){ | ||
case self::ACTION_VERIFIED: | ||
$player = $data["player"]; | ||
$this->data[strtolower($player)] = $data["discordId"]; | ||
if(($player = $this->getServer()->getPlayerExact($player)) !== null){ | ||
$player->sendMessage(TextFormat::GREEN . "You've verified!"); | ||
} | ||
break; | ||
case self::ACTION_UNVERIFIED: | ||
$player = $data["player"]; | ||
if(isset($this->data[strtolower($player)])){ | ||
unset($this->data[strtolower($player)]); | ||
if(($player = $this->getServer()->getPlayerExact($player)) !== null){ | ||
$player->sendMessage(TextFormat::GREEN . "You've unverified!"); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
}), 5); | ||
|
||
$this->getServer()->getCommandMap()->register("discordverify", new VerifyCommand()); | ||
} | ||
|
||
public function onDisable() : void{ | ||
if($this->canThreadClose){ | ||
$this->thread->shutdown(); | ||
while($this->thread->isRunning()){ | ||
// SAFETY THREAD CLOSE | ||
} | ||
} | ||
file_put_contents($this->getDataFolder() . "verified_players.json", json_encode($this->data)); | ||
} | ||
|
||
public function addQueue(array $data) : void{ | ||
$this->inboundQueue[] = $data; | ||
} | ||
|
||
public function isVerified(string $player) : bool{ | ||
return isset($this->data[strtolower($player)]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TeamBixby\DiscordVerify\command; | ||
|
||
use pocketmine\command\CommandSender; | ||
use pocketmine\command\PluginCommand; | ||
use pocketmine\Player; | ||
use pocketmine\utils\TextFormat; | ||
use pocketmine\utils\UUID; | ||
use TeamBixby\DiscordVerify\DiscordVerify; | ||
use function substr; | ||
|
||
class VerifyCommand extends PluginCommand{ | ||
|
||
public function __construct(){ | ||
parent::__construct("verify", DiscordVerify::getInstance()); | ||
$this->setPermission("discordverify.command.use"); | ||
} | ||
|
||
public function execute(CommandSender $sender, string $commandLabel, array $args) : bool{ | ||
if(!$this->testPermission($sender)){ | ||
return false; | ||
} | ||
if(!$sender instanceof Player){ | ||
$sender->sendMessage(TextFormat::RED . "You can't use this command on console."); | ||
return false; | ||
} | ||
if(DiscordVerify::getInstance()->isVerified($sender->getName())){ | ||
$sender->sendMessage(TextFormat::RED . "You are already verified."); | ||
return false; | ||
} | ||
DiscordVerify::getInstance()->addQueue([ | ||
"player" => $sender->getName(), | ||
"random_token" => $token = substr(UUID::fromRandom()->toString(), 0, 6) | ||
]); | ||
$sender->sendMessage(TextFormat::GREEN . "Use the verify command on Discord server using " . $token); | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TeamBixby\DiscordVerify\util; | ||
|
||
use Exception; | ||
|
||
class DiscordException extends Exception{ | ||
|
||
public static function wrap(string $message) : DiscordException{ | ||
return new DiscordException($message); | ||
} | ||
} |