diff --git a/README.md b/README.md
index 6adb974..25a6120 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,44 @@
-SimpleAuthHelper
-================
+# SimpleAuthHelper
-* Summary: Simplifies the SimpleAuth login process
-* Dependency Plugins: n/a
-* PocketMine-MP version: 1.4 - API 1.10.0
+* Summary: Simplifies the way people authenticate to servers
+* Dependency Plugins: SimpleAuth
+* PocketMine-MP version: 1.5 (API:1.12.0)
* DependencyPlugins: SimpleAuth
* OptionalPlugins: -
* Categories: General
* Plugin Access: Commands
* WebSite: [github](https://github.com/alejandroliu/pocketmine-plugins/tree/master/SimpleAuthHelper)
-Overview
---------
+## Overview
-Very simple plugin that simplifies the login process... Instead of
+
+
+
+**DO NOT POST QUESTION/BUG-REPORTS/REQUESTS IN THE REVIEWS**
+
+It is difficult to carry a conversation in the reviews. If you
+have a question/bug-report/request please use the
+[Thread](http://forums.pocketmine.net/threads/simpleauthhelper.8074/) for
+that. You are more likely to get a response and help that way.
+
+_NOTE:_
+
+This documentation was last updated for version **2.0.1**.
+
+
+You can also download this plugin from this [page](https://github.com/alejandroliu/pocketmine-plugins/releases/tag/SimpleAuthHelper-2.0.1).
+
+
+
+A plugin that simplifies the login process... Instead of
asking for commands, users simply chat away...
-### Register process
+I also provides for a number of tweaks that can improve the usability of
+[SimpleAuth](https://forums.pocketmine.net/plugins/simpleauth.4/).
+
+#### Register process
Player connects for the first time. They are prompted to enter a
*NEW* password. They enter their password directly, without having to
@@ -27,64 +47,133 @@ enter */register*.
They are asked for the password again to confirm. They re-enter their
password (again without */register*).
-### Login process
+#### Login process
Player connects again. They are prompted to enter their login
password. They type their login password directly (without
*/login*). And they are in.
-Documentation
--------------
-
-As a bonus, it can start a player with initial inventory upon
-registration. This is configured through the `nest-egg` setting.
+## Documentation
### Commands
-* *chpwd* __
- Used by players to change their passwords.
-* *resetpwd* __
- Used by ops to reset a players password. This actually unregisters
- the password.
+* *chpwd* _<old-pwd>_
+ * Used by players to change their passwords.
+* *resetpwd* _<player>_
+ * Used by ops to reset a players password. This actually unregisters
+ the password.
+* *preregister* _<player>_ _<passwd>_
+ * Used by ops to pre-register players.
+* *logout*
+ * De-authenticates a player.
+
+### Permission Nodes
+
+* simpleauthhelper.command.chpwd : Allow users to change passwords
+* simpleauthhelper.command.logout : Allow users to logout
+* simpleauthhelper.command.resetpwd : Allow ops to reset other's passwords
+ (Defaults to Op)
+* simpleauthhelper.command.prereg : Allow ops to pre-register users
+ (Defaults to Op)
+
### Configuration
- ---
- messages:
- re-enter pwd: 'Please re-enter password to confirm:'
- passwords dont match: |-
- Passwords do not match.
- Please try again!
- Enter a new password:
- register ok: You have been registered!
- no spaces: |-
- Password should not contain spaces
- or tabs
- not name: Password should not be your name
- too many login: You have attempted to login too many times.
- login timeout: Login timer expired!
- nest-egg:
- - "272:0:1"
- - "17:0:16"
- - "364:0:5"
- - "266:0:10"
- max-attempts: 5
- login-timeout: 60
- auto-ban: false
- ...
-
-* The section `messages` can be used to configure displayed texts.
-* `nest-egg` section contains list of items that will be given to the
- player upon registration.
-* `max-attempts` counts the number of tries to login.
-* `login-timeout` will kick the player out if not authenticated in
- that number of seconds.
-* `auto-ban`: If set to true it will automatically ban the IP of a
- player that does too many login attempts.
-
-Changes
--------
+Configuration is through the `config.yml` file.
+The following sections are defined:
+
+#### main
+
+
+Configure the different features used by this plugin.
+
+ feature: true|false
+
+If `true` the feature is enabled. if `false` the feature is disabled.
+* max-attemps: kick player after this many login attempts. NOTE: This conflicts with SimpleAuth's blockAfterFail setting
+* login-timeout: must authenticate within this number of seconds
+* leet-mode: lets players use also /login and /register
+* chat-protect: prevent player to display their password in chat
+* hide-unauth: EXPERIMENTAL, hide unauthenticated players
+* event-fixer: EXPERIMENTAL, cancels additional events for unauthenticated players
+* hack-login-perms: EXPERIMENTAL, overrides login permisions to make sure players can login
+* hack-register-perms: EXPERIMENTAL, overrides register permisions to make sure players can register
+* db-monitor: EXPERIMENTAL, enable database server monitoring
+* monitor-settings: Configure database monitor settings
+#### monitor-settings
+
+* canary-account: account to query this account is tested to check database proper operations
+* check-interval: how to often to check database (seconds)
+
+
+## Translations
+
+This plugin will honour the server language configuration. The
+languages currently available are:
+
+* English
+* Spanish
+
+You can provide your own message file by creating a file called
+`messages.ini` in the plugin config directory. Check
+[github](https://github.com/alejandroliu/pocketmine-plugins/tree/master/SimpleAuthHelper/resources/messages)
+for sample files.
+
+## Player pre-registration
+
+It is possible to implement a web based pre-registration system with this
+plugin.
+
+1. *rcon* must be enabled on the PocketMine server.
+2. web server must be able to send *rcon* commands to PocketMine.
+3. Enable the *whitelist* functionality in PocketMine.
+4. Install *SimpleAuth* and *SimpleAuthHelper*.
+5. **Optionally** install *PurePerms* and disable
+ `simpleauthhelper.command.chpwd` permission. You probably want
+ users to change passwords from the web site.
+6. Whenever a user registers in web site, the web site script uses *rcon*
+ to send the follwoing:
+ - whitelist add _player_
+ - preregister _player_ _passwd_
+7. Whenever a user changes password in web site, we use *rcon* with:
+ - resetpwd _player_
+ - preregister _player_ _passwd_
+
+## Database Monitor
+
+This module is responsible for monitoring the SimpleAuth data provider
+to make sure that it is up and running and disable logins if it is not
+available.
+
+It kicks off a background task that will poll the SimpleAuth data provider
+by trying to retrieve the data from the "canary-account". It is important
+that you have configured and have working SimpleAuth provider the first
+time you enable the database monitor. This is because the "canary-account"
+needs to be created (if it doesn't exist already).
+
+On a regular interval, the SimpleAuth
+data provider is checked. If it is not running, all unauthenticated players
+are kicked and any new joins are not allowed.
+
+## Issues
+
+* Event Fixer: Crafting canceling doesn't work
+
+# Changes
+
+* 2.0.1: language defaults
+ - make sure that languages default to English (reported by @minebuilder0110)
+* 2.0.0: Major upgrade
+ - uses now a common translation library
+ - Removed little used feature: nest-egg
+ - leet-mode also works for /register.
+ - Removed auto-ban. It is now done in SimpleAuth.
+ - Added support for hiding unauthenticated players (Suggested by @CaptainKenji17)
+ - Added pre-register and logout command
+ - forces permissions to be set
+ - Added a task to monitor database server status
+ - Thanks @rvachvg for helping debug this.
* 1.2.3: Security improvements
- prevent user from chatting away their password
- add option so that players can also use "/login" to login.
@@ -93,7 +182,7 @@ Changes
* 1.2.1: CallbackTask deprecation
* Removed CallbackTask deprecation warnings
* 1.2.0: max-logins
- * Suggestion from MCPEPIG
+ * Suggestion from @MCPEPIG
- kick user out after `max-attempts`.
- Added a chpwd command.
* Kick user out if not authenticated after `timeout` seconds.
@@ -103,8 +192,7 @@ Changes
* Messages can be configured.
* 1.0.0: First release
-Copyright
----------
+# Copyright
SimpleAuthHelper
Copyright (C) 2015 Alejandro Liu
@@ -122,3 +210,4 @@ Copyright
You should have received a copy of the GNU General Public License
along with this program. If not, see .
+
diff --git a/plugin.yml b/plugin.yml
index a01ad84..5953e5b 100644
--- a/plugin.yml
+++ b/plugin.yml
@@ -1,11 +1,11 @@
main: aliuly\helper\Main
-api: 1.10.0
+api: 1.12.0
load: POSTWORLD
depend: [SimpleAuth]
name: SimpleAuthHelper
description: Simplifies the way people authenticate to servers
-version: 1.2.3
+version: 2.0.1
author: aliuly
commands:
@@ -13,15 +13,32 @@ commands:
description: "Change password"
usage: "/chpwd "
permission: simpleauthhelper.command.chpwd
+ aliases: [passwd]
resetpwd:
description: "Reset password"
usage: "/resetpwd "
permission: simpleauthhelper.command.resetpwd
+ aliases: [resetpw]
+ preregister:
+ description: "pre-register player"
+ usage: "/preregister "
+ permission: simpleauthhelper.command.prereg
+ aliases: [prereg]
+ logout:
+ description: "logout player"
+ usage: "/logout"
+ permission: simpleauthhelper.command.logout
permissions:
simpleauthhelper.command.chpwd:
default: true
description: "Allow users to change passwords"
+ simpleauthhelper.command.logout:
+ default: true
+ description: "Allow users to logout"
simpleauthhelper.command.resetpwd:
default: op
description: "Allow ops to reset other's passwords"
+ simpleauthhelper.command.prereg:
+ default: op
+ description: "Allow ops to pre-register users"
diff --git a/resources/messages/eng.ini b/resources/messages/eng.ini
new file mode 100644
index 0000000..8997756
--- /dev/null
+++ b/resources/messages/eng.ini
@@ -0,0 +1,51 @@
+; eng.ini
+"%1%-mode"=""
+"%1% already registered"=""
+"%1% has just joined"=""
+"%1% unauthenticated players were kicked"=""
+"%1% unregistered"=""
+"**CENSORED**"=""
+""=""
+"Adventure"=""
+"Creative"=""
+"DBM Error: %1%"=""
+"Database connectivity restored!"=""
+"Database is experiencing technical difficulties"=""
+"Detected loss of database connectivity!"=""
+"Disabling SimpleAuth"=""
+"Enabling SimpleAuth"=""
+"Fixing %1% for %2%"=""
+"LOST DATABASE CONNECTION!"=""
+"Restored database connection"=""
+"SimpleAuthHelper has been disabled"=""
+"SimpleAuth not found!"=""
+"Spectator"=""
+"Survival"=""
+"This command only works in-game."=""
+"Unable to find SimpleAuth"=""
+"Unable to unregister %1%"=""
+"You are no longer registered!"=""
+"You can only do this in-game"=""
+"You do not have permission to do that."=""
+"auth error"="Authentication error. Try again later!"
+"chat protected"="Do not send your password on the chat window"
+"chpwd error"="Old password does not match"
+"chpwd msg"="Enter your new password:"
+"chpwd ok"="Password changed succesfully"
+"error registering %1%"=""
+"login first"=""
+"login timeout"="Login timer expired!"
+"logout completed"=""
+"no spaces"="Password should not contain spaces or tabs"
+"not name"="Password should not be your name"
+"one unauthenticated player was kicked"=""
+"passwords dont match"="Passwords do not match.\nPlease try again!\nEnter a new password:"
+"re-enter pwd"="Please re-enter password to confirm:"
+"register.error.password %1%"="Your password should be at least %1% characters"
+"registered %1%"=""
+"register first"="You must first be registered"
+"register ok"="You have been registered!"
+"registration error"="Registration error. Try again later!"
+"snob login"="Actually, you don't really need to type /login"
+"snob register"="Actually, you don't really need to type /register"
+"too many logins"="You have attempted to login too many times."
diff --git a/resources/messages/messages.ini b/resources/messages/messages.ini
new file mode 100644
index 0000000..5604421
--- /dev/null
+++ b/resources/messages/messages.ini
@@ -0,0 +1,51 @@
+; messages.ini
+"%1%-mode"=""
+"%1% already registered"=""
+"%1% has just joined"=""
+"%1% unauthenticated players were kicked"=""
+"%1% unregistered"=""
+"**CENSORED**"=""
+""=""
+"Adventure"=""
+"Creative"=""
+"DBM Error: %1%"=""
+"Database connectivity restored!"=""
+"Database is experiencing technical difficulties"=""
+"Detected loss of database connectivity!"=""
+"Disabling SimpleAuth"=""
+"Enabling SimpleAuth"=""
+"Fixing %1% for %2%"=""
+"LOST DATABASE CONNECTION!"=""
+"Restored database connection"=""
+"SimpleAuthHelper has been disabled"=""
+"SimpleAuth not found!"=""
+"Spectator"=""
+"Survival"=""
+"This command only works in-game."=""
+"Unable to find SimpleAuth"=""
+"Unable to unregister %1%"=""
+"You are no longer registered!"=""
+"You can only do this in-game"=""
+"You do not have permission to do that."=""
+"auth error"=""
+"chat protected"=""
+"chpwd error"=""
+"chpwd msg"=""
+"chpwd ok"=""
+"error registering %1%"=""
+"login first"=""
+"login timeout"=""
+"logout completed"=""
+"no spaces"=""
+"not name"=""
+"one unauthenticated player was kicked"=""
+"passwords dont match"=""
+"re-enter pwd"=""
+"register.error.password %1%"=""
+"registered %1%"=""
+"register first"=""
+"register ok"=""
+"registration error"=""
+"snob login"=""
+"snob register"=""
+"too many logins"=""
diff --git a/resources/messages/spa.ini b/resources/messages/spa.ini
new file mode 100644
index 0000000..d779ec6
--- /dev/null
+++ b/resources/messages/spa.ini
@@ -0,0 +1,51 @@
+; spa.ini
+"%1%-mode"="modo-%1%"
+"%1% already registered"="%1% ya esta registrado"
+"%1% has just joined"="%1% se ha conectado al juego"
+"%1% unauthenticated players were kicked"="%1% jugadores deconocidos retirados"
+"%1% unregistered"="%1% de-registrado"
+"**CENSORED**"="**CENSURADO**"
+""="no"
+"Adventure"="Aventura"
+"Creative"="Creativo"
+"DBM Error: %1%"="Error DBM: %1%"
+"Database connectivity restored!"="Acceso a base de datos restaurado"
+"Database is experiencing technical difficulties"="Base datos en problemas"
+"Detected loss of database connectivity!"="Acceso a base de datos perdido"
+"Disabling SimpleAuth"="Deshabilitando SimpleAuth"
+"Enabling SimpleAuth"="Rehabilitando SimpleAuth"
+"Fixing %1% for %2%"="Arreglando %1% para %2%"
+"LOST DATABASE CONNECTION!"="SE PERDIO LA BASE DE DATOS"
+"Restored database connection"="Base de datos retaurada"
+"SimpleAuthHelper has been disabled"="SimpleAuthHelper ha sido des-habilitado"
+"SimpleAuth not found!"="No se encotró SimpleAuth!"
+"Spectator"="Espectador"
+"Survival"="Supervivencia"
+"This command only works in-game."="Esto solo funciona dentro del juego"
+"Unable to find SimpleAuth"="No encuentro SimleAuth"
+"Unable to unregister %1%"="No se puede de-registrar a %1%"
+"You are no longer registered!"="Usted ya no está registrado!"
+"You can only do this in-game"="Solo puede hacer esto en el juego"
+"You do not have permission to do that."="No tiene permiso para hacer eso"
+"auth error"="No se pudo identificar. Pruebe más tarde!"
+"chat protected"="No ingrese su contraseña en la pantalla"
+"chpwd error"="No concuerda con su contraseña anterior"
+"chpwd msg"="Ingrese una nueva contraseña"
+"chpwd ok"="Su contraseña ha sido canbiada"
+"error registering %1%"="Error registrando a %1%"
+"login first"="Identifiquese primero"
+"login timeout"="Se demoró mucho en indentificarse!"
+"logout completed"="Se des-identificado"
+"no spaces"="contraseña no puede tener espacios o tabs"
+"not name"="no use su nombre como contraseña"
+"one unauthenticated player was kicked"="un jugador desconocido retirado"
+"passwords dont match"="contraseñas no concuerdan.\nPor favor intente de nuevo.\nIngrese nueva contraseña:"
+"re-enter pwd"="Vuelva a ingresar su contraseña para confirmar:"
+"register.error.password %1%"="su contraseña es muy corta (necesita %1% letras)"
+"registered %1%"="Registrando %1%"
+"register first"="Tiene que registrarse primero"
+"register ok"="Usted ha sido registrado."
+"registration error"="No se pudo registrar. Intente más tarde!"
+"snob login"="En realidad, no necesita ingresar /login"
+"snob register"="En realidad, no necesita ingresar /register"
+"too many logins"="Ha intentado indentificarse demasiadas veces"
diff --git a/src/aliuly/helper/DbMonitorTask.php b/src/aliuly/helper/DbMonitorTask.php
new file mode 100644
index 0000000..83203b0
--- /dev/null
+++ b/src/aliuly/helper/DbMonitorTask.php
@@ -0,0 +1,154 @@
+ "account to query",//this account is tested to check database proper operations
+ "canary-account" => "test user",
+ "# check-interval" => "how to often to check database (seconds)",
+ "check-interval" => 600,
+ ];
+ }
+ public function __construct(HelperPlugin $owner,$cfg){
+ parent::__construct($owner);
+ $this->canary = $cfg["canary-account"];
+ if ($owner->auth->isEnabled()) {
+ $this->dbm = $owner->auth->getDataProvider();
+ $this->ok = true; // Assume things are OK...
+ if (!$this->pollDB()) {
+ // If this fails then canary account doesn't exist yet... create it
+ $player = $this->getOwner()->getServer()->getOfflinePlayer($this->canary);
+ if ($player === null) {
+ throw new \RuntimeException("canary account definition error!");
+ return;
+ }
+ $err = $this->dbm->registerPlayer($player,"N/A");
+ if ($err === null) {
+ throw new \RuntimeException("Unable to register canary account!");
+ }
+ }
+ } else {
+ $this->ok = false;
+ }
+
+ $owner->getServer()->getScheduler()->scheduleRepeatingTask($this,$cfg["check-interval"]*20);
+ $owner->getServer()->getPluginManager()->registerEvents($this, $owner);
+ }
+ private function setStatus($mode) {
+ if ($this->ok === $mode) return;
+ $this->ok = $mode;
+ if ($mode) {
+ $this->getOwner()->getLogger()->info(mc::_("Restored database connection"));
+ $this->getOwner()->getServer()->broadcastMessage(TextFormat::GREEN.mc::_("Database connectivity restored!"));
+ return;
+ }
+ $this->getOwner()->getLogger()->error(mc::_("LOST DATABASE CONNECTION!"));
+ $this->getOwner()->getServer()->broadcastMessage(TextFormat::RED.mc::_("Detected loss of database connectivity!"));
+ // Kick all unregistered players...
+ $auth = $this->getOwner()->getServer()->getPluginManager()->getPlugin("SimpleAuth");
+ if ($auth !== null) {
+ $cnt = 0;
+ foreach ($this->getOwner()->getServer()->getOnlinePlayers() as $ll) {
+ if (!$auth->isPlayerAuthenticated($ll)) {
+ $this->delayedKick($ll,mc::_("Database is experiencing technical difficulties"));
+ ++$cnt;
+ }
+ }
+ if ($cnt)
+ $this->getOwner()->getServer()->broadcastMessage(
+ TextFormat::BLUE.
+ mc::n(
+ mc::_("one unauthenticated player was kicked"),
+ mc::_("%1% unauthenticated players were kicked", $cnt),
+ $cnt
+ )
+ );
+ return;
+ }
+ }
+ private function enableAuth($mgr,$auth) {
+ if ($auth === null) return false; // OK, this is weird!
+ if ($auth->isEnabled()) return true;
+ $this->getOwner()->getLogger()->info(mc::_("Enabling SimpleAuth"));
+ $mgr->enablePlugin($auth);
+ if (!$auth->isEnabled()) return false;
+ $this->dbm = $auth->getDataProvider();
+ return true;
+ }
+ private function pollDB() {
+ $player = $this->getOwner()->getServer()->getOfflinePlayer($this->canary);
+ if ($player == null) return true;//Automatically assume things are OK :)
+ try {
+ return $this->dbm->isPlayerRegistered($player);
+ } catch (\Exception $e) {
+ $this->getOwner()->getLogger()->error(mc::_("DBM Error: %1%",$e->getMessage()));
+ }
+ return false;
+ }
+
+ public function onRun($currentTicks){
+ $mgr = $this->getOwner()->getServer()->getPluginManager();
+ $auth = $mgr->getPlugin("SimpleAuth");
+ if ($auth === null) return; // OK, this is weird!
+
+ if (!$auth->isEnabled()) {
+ if (!$this->enableAuth($mgr,$auth)) return; // Ouch...
+ }
+ if ($this->pollDB()) {
+ $this->setStatus(true);
+ return;
+ }
+ /*
+ * Lost connection to database...
+ */
+ $this->setStatus(false);
+ /*
+ * let's try to reconnect by resetting SimpleAuth
+ */
+ if ($auth->isEnabled()) {
+ $this->getOwner()->getLogger()->info(mc::_("Disabling SimpleAuth"));
+ $mgr->disablePlugin($auth);
+ }
+ if (!$auth->isEnabled()) {
+ $this->getOwner()->getLogger()->info(mc::_("Enabling SimpleAuth"));
+ if (!$this->enableAuth($mgr,$auth)) return; // Ouch...
+ }
+ if ($this->pollDB()) $this->setStatus(true);
+ }
+ public function doKick($n,$msg) {
+ $pl = $this->getOwner()->getServer()->getPlayer($n);
+ if ($pl === null) return;
+ $pl->kick($msg);
+ }
+ private function delayedKick($pl,$msg) {
+ $this->getOwner()->getServer()->getScheduler()->scheduleDelayedTask(
+ new PluginCallbackTask($this->getOwner(),[$this,"doKick"],[$pl->getName(),$msg]),
+ 10
+ );
+ }
+ public function onConnect(PlayerLoginEvent $ev) {
+ $this->onRun(0);
+ }
+ public function onJoin(PlayerJoinEvent $ev) {
+ if ($this->ok) return;
+ $this->delayedKick($ev->getPlayer(),mc::_("Database is experiencing technical difficulties"));
+ }
+}
diff --git a/src/aliuly/helper/EventListener.php b/src/aliuly/helper/EventListener.php
new file mode 100644
index 0000000..1cd80b8
--- /dev/null
+++ b/src/aliuly/helper/EventListener.php
@@ -0,0 +1,177 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+*/
+namespace aliuly\helper;
+
+use pocketmine\event\block\BlockBreakEvent;
+use pocketmine\event\block\BlockPlaceEvent;
+use pocketmine\event\inventory\InventoryOpenEvent;
+use pocketmine\event\inventory\InventoryPickupItemEvent;
+use pocketmine\event\Listener;
+use pocketmine\event\player\PlayerCommandPreprocessEvent;
+use pocketmine\event\player\PlayerDropItemEvent;
+use pocketmine\event\player\PlayerInteractEvent;
+use pocketmine\event\player\PlayerItemConsumeEvent;
+use pocketmine\event\player\PlayerJoinEvent;
+use pocketmine\event\player\PlayerMoveEvent;
+use pocketmine\event\player\PlayerPreLoginEvent;
+use pocketmine\event\player\PlayerQuitEvent;
+use pocketmine\event\player\PlayerRespawnEvent;
+use pocketmine\event\entity\EntityDamageEvent;
+use pocketmine\event\entity\EntityDamageByEntityEvent;
+use pocketmine\event\inventory\CraftItemEvent;
+use pocketmine\Player;
+use pocketmine\inventory\PlayerInventory;
+use aliuly\helper\Main as HelperPlugin;
+
+
+class EventListener implements Listener{
+ /** @var SimpleAuth */
+ private $auth;
+ private $owner;
+
+ public function __construct(HelperPlugin $owner){
+ $this->auth = $owner->auth;
+ $owner->getServer()->getPluginManager()->registerEvents($this, $owner);
+ }
+
+ /**
+ * @priority LOWEST
+ */
+ public function onCrafting(CraftItemEvent $event){
+ foreach ($event->getTransaction()->getInventories() as $inv) {
+ if (($inv instanceof PlayerInventory)) continue;
+ $player = $inv->getHolder();
+ if (!$this->auth->isPlayerAuthenticated($inv->getHolder())) {
+ $event->setCancelled(true);
+ return;
+ }
+ }
+ }
+
+ /**
+ * @param PlayerMoveEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onPlayerMove(PlayerMoveEvent $event){
+ if(!$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ if(!$event->getPlayer()->hasPermission("simpleauth.move")){
+ $event->setCancelled(true);
+ $event->getPlayer()->onGround = true;
+ }
+ }
+ }
+
+ /**
+ * @param PlayerInteractEvent $event
+ *
+ * @priority HIGHEST
+ */
+ public function onPlayerInteract(PlayerInteractEvent $event){
+
+ if(!$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ $event->setCancelled(true);
+ }
+ }
+
+ /**
+ * @param PlayerDropItemEvent $event
+ *
+ * @priority HIGHEST
+ */
+ public function onPlayerDropItem(PlayerDropItemEvent $event){
+ if(!$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ $event->setCancelled(true);
+ }
+ }
+
+ /**
+ * @param PlayerItemConsumeEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onPlayerItemConsume(PlayerItemConsumeEvent $event){
+ if(!$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ $event->setCancelled(true);
+ }
+ }
+
+ /**
+ * @param EntityDamageEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onEntityDamage(EntityDamageEvent $event){
+ if($event->getEntity() instanceof Player and !$this->auth->isPlayerAuthenticated($event->getEntity())){
+ $event->setCancelled(true);
+ return;
+ }
+ // Also check if we are inflicting damage to others
+ if(!($event instanceof EntityDamageByEntityEvent)) return;
+ $giver = $event->getDamager();
+ if (!($giver instanceof Player)) return;
+ if (!$this->auth->isPlayerAuthenticated($giver)) {
+ $event->setCancelled(true);
+ return;
+ }
+ }
+
+ /**
+ * @param BlockBreakEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onBlockBreak(BlockBreakEvent $event){
+ if($event->getPlayer() instanceof Player and !$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ $event->setCancelled(true);
+ }
+ }
+
+ /**
+ * @param BlockPlaceEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onBlockPlace(BlockPlaceEvent $event){
+ if($event->getPlayer() instanceof Player and !$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ $event->setCancelled(true);
+ }
+ }
+
+ /**
+ * @param InventoryOpenEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onInventoryOpen(InventoryOpenEvent $event){
+ if(!$this->auth->isPlayerAuthenticated($event->getPlayer())){
+ $event->setCancelled(true);
+ }
+ }
+
+ /**
+ * @param InventoryPickupItemEvent $event
+ *
+ * @priority LOWEST
+ */
+ public function onPickupItem(InventoryPickupItemEvent $event){
+ $player = $event->getInventory()->getHolder();
+ if($player instanceof Player and !$this->auth->isPlayerAuthenticated($player)){
+ $event->setCancelled(true);
+ }
+ }
+}
diff --git a/src/aliuly/helper/Main.php b/src/aliuly/helper/Main.php
index 5e9938c..0ec4b57 100644
--- a/src/aliuly/helper/Main.php
+++ b/src/aliuly/helper/Main.php
@@ -1,4 +1,16 @@
getDataFolder())) mkdir($this->getDataFolder());
+ if (mc::plugin_init($this,$this->getFile()) === false) {
+ file_put_contents($this->getDataFolder()."messages.ini",MPMU::getResourceContents($this,"messages/eng.ini")."\n\"\"=\"yes\"\n");
+ mc::plugin_init($this,$this->getFile());
+ $this->getLogger()->error(TextFormat::RED."Your selected language \"".$this->getServer()->getProperty("settings.language")."\" is not supported");
+ $this->getLogger()->error(TextFormat::YELLOW."Creating a custom \"messages.ini\" with English strings");
+ $this->getLogger()->error(TextFormat::AQUA."Please consider translating and submitting a translation");
+ $this->getLogger()->error(TextFormat::AQUA."to the developer");
+ } else {
+ if (mc::_("") === "yes") {
+ $this->getLogger()->error(TextFormat::RED."Your selected language \"".$this->getServer()->getProperty("settings.language")."\" is not supported");
+ $this->getLogger()->error(TextFormat::AQUA."Please consider translating \"messages.ini\"");
+ $this->getLogger()->error(TextFormat::AQUA."and submitting a translation to the developer");
+ }
+ }
$this->auth = $this->getServer()->getPluginManager()->getPlugin("SimpleAuth");
if (!$this->auth) {
- $this->getLogger()->info(TextFormat::RED."Unable to find SimpleAuth");
+ $this->getLogger()->error(TextFormat::RED.mc::_("Unable to find SimpleAuth"));
+ throw new \RuntimeException("Missing Dependancy");
return;
}
- if (!is_dir($this->getDataFolder())) mkdir($this->getDataFolder());
+
$defaults = [
"version" => $this->getDescription()->getVersion(),
- "messages" => [
- "re-enter pwd" => "Please re-enter password to confirm:",
- "passwords dont match" => "Passwords do not match.\nPlease try again!\nEnter a new password:",
- "register ok" => "You have been registered!",
- "no spaces" => "Password should not contain spaces or tabs",
- "not name" => "Password should not be your name",
- "too many logins" => "You have attempted to login too many times.",
- "login timeout" => "Login timer expired!",
- "register first" => "You must first be registered",
- "chpwd msg" => "Enter your new password:",
- "chpwd error" => "Old password does not match",
- "chpwd ok" => "Password changed succesfully",
- "registration error" => "Registration error. Try again later!",
- "auth error" => "Authentication error. Try again later!",
- "chat protected" => "Do not send your password on the chat window",
- "snob login" => "Actually, you don't really need to type /login",
- ],
- "nest-egg" => [
- "STONE_SWORD:0:1",
- "WOOD:0:16",
- "COOKED_BEEF:0:5",
- "GOLD_INGOT:0:10",
- ],
+ "# max-attemps" => "kick player after this many login attempts. ",// NOTE: This conflicts with SimpleAuth's blockAfterFail setting
"max-attempts" => 5,
+ "# login-timeout" => "must authenticate within this number of seconds",
"login-timeout" => 60,
- "auto-ban" => false,
- "lamer-mode" => false,
+ "# leet-mode" => "lets players use also /login and /register",
+ "leet-mode" => true,
+ "# chat-protect" => "prevent player to display their password in chat",
"chat-protect" => false,
+ "# hide-unauth" => "EXPERIMENTAL, hide unauthenticated players",
+ "hide-unauth" => false,
+ "# event-fixer" => "EXPERIMENTAL, cancels additional events",// for unauthenticated players
+ "event-fixer" => false,
+ "# hack-login-perms" => "EXPERIMENTAL, overrides login permisions",//to make sure players can login
+ "hack-login-perms" => false,
+ "# hack-register-perms" => "EXPERIMENTAL, overrides register permisions",//to make sure players can register
+ "hack-register-perms" => false,
+ "# db-monitor" => "EXPERIMENTAL, enable database server monitoring",
+ "db-monitor" => false,
+ "# monitor-settings" => "Configure database monitor settings",
+ "monitor-settings" => DbMonitorTask::defaults(),
];
- if (file_exists($this->getDataFolder()."config.yml")) {
- unset($defaults["nest-egg"]);
- }
$this->cfg=(new Config($this->getDataFolder()."config.yml",
Config::YAML,$defaults))->getAll();
- $this->getServer()->getPluginManager()->registerEvents($this, $this);
+ $this->getServer()->getPluginManager()->registerEvents($this,$this);
+ if ($this->cfg["event-fixer"]) {
+ $this->listener =new EventListener($this);
+ }
+ if ($this->cfg["hack-login-perms"] || $this->cfg["hack-register-perms"]) {
+ $this->permshacker = new PermsHacker($this,$this->cfg["hack-login-perms"],$this->cfg["hack-register-perms"]);
+ }
+ if ($this->cfg["db-monitor"]) {
+ $this->monitor = new DbMonitorTask($this,$this->cfg["monitor-settings"]);
+ }
$this->pwds = [];
}
//////////////////////////////////////////////////////////////////////
@@ -79,9 +122,30 @@ public function onPlayerQuit(PlayerQuitEvent $ev) {
if (isset($this->chpwd[$n])) unset($this->chpwd[$n]);
}
public function onPlayerJoin(PlayerJoinEvent $ev) {
- if ($this->cfg["login-timeout"] == 0) return;
- $n = $ev->getPlayer()->getName();
- $this->getServer()->getScheduler()->scheduleDelayedTask(new PluginCallbackTask($this,[$this,"checkTimeout"],[$n]),$this->cfg["login-timeout"]*20);
+ if ($this->cfg["login-timeout"] !== 0) {
+ $n = $ev->getPlayer()->getName();
+ $this->getServer()->getScheduler()->scheduleDelayedTask(new PluginCallbackTask($this,[$this,"checkTimeout"],[$n]),$this->cfg["login-timeout"]*20);
+ }
+ if ($this->cfg["hide-unauth"]) {
+ $p = $ev->getPlayer();
+ foreach($this->getServer()->getOnlinePlayers() as $online){
+ $online->hidePlayer($p);
+ $p->hidePlayer($online);
+ }
+ $ev->setJoinMessage("");
+ //
+ }
+ }
+ public function onAuthenticate(PlayerAuthenticateEvent $ev) {
+ if (!$this->cfg["hide-unauth"]) return;
+ $pl = $ev->getPlayer();
+ $this->getServer()->broadcastMessage(TextFormat::YELLOW.mc::_("%1% has just joined", $pl->getDisplayName()));
+ foreach($this->getServer()->getOnlinePlayers() as $online){
+ $online->showPlayer($pl);
+ if ($this->auth->isPlayerAuthenticated($online)) {
+ $pl->showPlayer($online);
+ }
+ }
}
/**
* @priority LOW
@@ -93,8 +157,8 @@ public function onPlayerCmd(PlayerCommandPreprocessEvent $ev) {
if ($this->auth->isPlayerAuthenticated($pl) && !isset($this->chpwd[$n])) {
if ($this->cfg["chat-protect"]) {
if ($this->authenticate($pl,$ev->getMessage())) {
- $pl->sendMessage($this->cfg["messages"]["chat protected"]);
- $ev->setMessage("**CENSORED**");
+ $pl->sendMessage(TextFormat::RED.mc::_("chat protected"));
+ $ev->setMessage(mc::_("**CENSORED**"));
$ev->setCancelled();
}
}
@@ -103,13 +167,17 @@ public function onPlayerCmd(PlayerCommandPreprocessEvent $ev) {
if (!$this->auth->isPlayerRegistered($pl) || isset($this->chpwd[$n])) {
if (!isset($this->pwds[$n])) {
+ if ($this->cfg["leet-mode"] && preg_match(self::RE_REGISTER,$ev->getMessage())) {
+ $pl->sendMessage(TextFormat::YELLOW.mc::_("snob register"));
+ $ev->setMessage(preg_replace(self::RE_REGISTER,'',$ev->getMessage()));
+ }
if (!$this->checkPwd($pl,$ev->getMessage())) {
$ev->setCancelled();
$ev->setMessage("~");
return;
}
$this->pwds[$n] = $ev->getMessage();
- $pl->sendMessage($this->cfg["messages"]["re-enter pwd"]);
+ $pl->sendMessage(TextFormat::AQUA.mc::_("re-enter pwd"));
$ev->setCancelled();
$ev->setMessage("~");
return;
@@ -118,7 +186,7 @@ public function onPlayerCmd(PlayerCommandPreprocessEvent $ev) {
unset($this->pwds[$n]);
$ev->setCancelled();
$ev->setMessage("~");
- $pl->sendMessage($this->cfg["messages"]["passwords dont match"]);
+ $pl->sendMessage(TextFormat::RED.mc::_("passwords dont match"));
return;
}
if (isset($this->chpwd[$n])) {
@@ -130,45 +198,35 @@ public function onPlayerCmd(PlayerCommandPreprocessEvent $ev) {
unset($this->pwds[$n]);
if (!$this->auth->unregisterPlayer($pl)) {
- $pl->sendMessage($this->cfg["messages"]["registration error"]);
+ $pl->sendMessage(TextFormat::RED.mc::_("registration error"));
return;
}
if (!$this->auth->registerPlayer($pl,$pw)) {
- $pl->kick($this->cfg["messages"]["registration error"]);
+ $pl->kick(mc::_("registration error"));
return;
}
- $pl->sendMessage($this->cfg["messages"]["chpwd ok"]);
+ $pl->sendMessage(TextFormat::GREEN.mc::_("chpwd ok"));
return;
}
// New user registration...
if (!$this->auth->registerPlayer($pl,$this->pwds[$n])) {
- $pl->kick($this->cfg["messages"]["registration error"]);
+ $pl->kick(mc::_("registration error"));
return;
}
if (!$this->auth->authenticatePlayer($pl)) {
- $pl->kick($this->cfg["messages"]["auth error"]);
+ $pl->kick(mc::_("auth error"));
return;
}
unset($this->pwds[$n]);
$ev->setMessage("~");
$ev->setCancelled();
- $pl->sendMessage($this->cfg["messages"]["register ok"]);
- if (isset($this->cfg["nest-egg"]) && !$pl->isCreative()) {
- // Award a nest egg to player...
- foreach ($this->cfg["nest-egg"] as $i) {
- $r = explode(":",$i);
- if (count($r) != 3) continue;
- $item = Item::fromString($r[0].":".$r[1]);
- $item->setCount(intval($r[2]));
- $pl->getInventory()->addItem($item);
- }
- }
+ $pl->sendMessage(TextFormat::GREEN.mc::_("register ok"));
return;
}
- if ($this->cfg["lamer-mode"]) {
+ if ($this->cfg["leet-mode"]) {
$msg = $ev->getMessage();
- if (preg_match('/^\s*\/login\s+/',$msg)) {
- $pl->sendMessage($this->cfg["messages"]["snob login"]);
+ if (preg_match(self::RE_LOGIN,$msg)) {
+ $pl->sendMessage(TextFormat::YELLOW.mc::_("snob login"));
} else {
$ev->setMessage("/login $msg");
}
@@ -188,7 +246,7 @@ public function onPlayerCmd(PlayerCommandPreprocessEvent $ev) {
public function checkTimeout($n) {
$pl = $this->getServer()->getPlayer($n);
if ($pl && !$this->auth->isPlayerAuthenticated($pl)) {
- $pl->kick($this->cfg["messages"]["login timeout"]);
+ $pl->kick(mc::_("login timeout"));
}
}
public function checkLoginCount($n) {
@@ -196,15 +254,7 @@ public function checkLoginCount($n) {
$pl = $this->getServer()->getPlayer($n);
if ($pl && !$this->auth->isPlayerAuthenticated($pl)) {
if ($this->pwds[$n] >= $this->cfg["max-attempts"]) {
- if ($this->cfg["auto-ban"]) {
- // OK banning use for trying to hack...
- $ip = $pl->getAddress();
- $this->getServer()->getIPBans()->addBan($ip,"Too many login attempts",null,"SimpleAuthHelper");
- $this->getServer()->blockAddress($ip,-1);
- $this->getServer()->broadcastMessage("[Helper] Banned IP Address $ip");
- }
-
- $pl->kick($this->cfg["messages"]["too many logins"]);
+ $pl->kick(mc::_("too many logins"));
unset($this->pwds[$n]);
}
return;
@@ -212,18 +262,19 @@ public function checkLoginCount($n) {
unset($this->pwds[$n]);
return;
}
- public function checkPwd($pl,$pwd) {
+ public function checkPwd($pl,$pwd, $name = null) {
if (preg_match('/\s/',$pwd)) {
- $pl->sendMessage($this->cfg["messages"]["no spaces"]);
+ $pl->sendMessage(TextFormat::RED.mc::_("no spaces"));
return false;
}
if (strlen($pwd) < $this->auth->getConfig()->get("minPasswordLength")){
- $pl->sendMessage($this->auth->getMessage("register.error.password"));
+ $pl->sendMessage(TextFormat::RED.mc::_("register.error.password %1%",
+ $this->auth->getConfig()->get("minPasswordLength")));
return false;
}
- if (strtolower($pl->getName()) == strtolower($pwd)) {
- $pl->sendMessage($this->cfg["messages"]["not name"]);
- return false;
+ if (strtolower($name === null ? $pl->getName() : $name) == strtolower($pwd)) {
+ $pl->sendMessage(TextFormat::RED.mc::_("not name"));
+ return false;
}
return true;
}
@@ -240,47 +291,85 @@ protected function authenticate($pl,$password) {
// Commands
//
//////////////////////////////////////////////////////////////////////
+ private function chpwd(CommandSender $sender, $oldpwd) {
+ if (!($sender instanceof Player)) {
+ $sender->sendMessage(TextFormat::RED.
+ mc::_("This command only works in-game."));
+ return true;
+ }
+ if(!$this->auth->isPlayerRegistered($sender)) {
+ $sender->sendMessage(TextFormat::YELLOW.mc::_("register first"));
+ return true;
+ }
+ if ($this->authenticate($sender,$oldpwd)) {
+ $this->chpwd[$sender->getName()] = $sender->getName();
+ $sender->sendMessage(TextFormat::AQUA.mc::_("chpwd msg"));
+ return true;
+ }
+ $sender->sendMessage(TextFormat::RED.mc::_("chpwd error"));
+ return false;
+ }
+ private function resetpwd($sender, $name) {
+ $player = $this->getServer()->getOfflinePlayer($name);
+ if($this->auth->unregisterPlayer($player)){
+ $sender->sendMessage(TextFormat::GREEN . mc::_("%1% unregistered",$name));
+ if($player instanceof Player){
+ $player->sendMessage(TextFormat::YELLOW.mc::_("You are no longer registered!"));
+ $this->auth->deauthenticatePlayer($player);
+ }
+ }else{
+ $sender->sendMessage(TextFormat::RED . mc::_("Unable to unregister %1%",$name));
+ }
+ return true;
+ }
+ private function logout($sender) {
+ if (!($sender instanceof Player)) {
+ $sender->sendMessage(TextFormat::RED.
+ mc::_("This command only works in-game."));
+ return true;
+ }
+ if(!$this->auth->isPlayerAuthenticated($sender)) {
+ $sender->sendMessage(TextFormat::YELLOW.mc::_("login first"));
+ return true;
+ }
+ $sender->sendMessage(TextFormat::GREEN.mc::_("logout completed"));
+ $this->auth->deauthenticatePlayer($sender);
+ return true;
+ }
+ private function prereg($sender,$name,$newpwd) {
+ $player = $this->getServer()->getOfflinePlayer($name);
+ if ($this->auth->isPlayerRegistered($player)) {
+ $sender->sendMessage(TextFormat::RED.mc::_("%1% already registered", $name));
+ return true;
+ }
+ if (!$this->checkPwd($sender,$newpwd,$name)) return true;
+ if ($this->auth->registerPlayer($player,$newpwd)) {
+ $sender->sendMessage(TextFormat::GREEN.mc::_("registered %1%", $name));
+ $sender->sendMessage("OK");
+ } else {
+ $sender->sendMessage(TextFormat::RED.mc::_("error registering %1%", $name));
+ }
+ return true;
+ }
public function onCommand(CommandSender $sender, Command $cmd, $label, array $args) {
if (!$this->auth) {
- $sender->sendMessage(TextFormat::RED."SimpleAuthHelper has been disabled");
- $sender->sendMessage(TextFormat::RED."SimpleAuth not found!");
+ $sender->sendMessage(TextFormat::RED.mc::_("SimpleAuthHelper has been disabled"));
+ $sender->sendMessage(TextFormat::RED.mc::_("SimpleAuth not found!"));
return true;
}
switch($cmd->getName()){
case "chpwd":
- if (!($sender instanceof Player)) {
- $sender->sendMessage(TextFormat::RED.
- "This command only works in-game.");
- return true;
- }
if (count($args) == 0) return false;
- if(!$this->auth->isPlayerRegistered($sender)) {
- $sender->sendMessage($this->cfg["messages"]["register first"]);
- return true;
- }
- if ($this->authenticate($sender,implode(" ", $args))) {
- $this->chpwd[$sender->getName()] = $sender->getName();
- $sender->sendMessage($this->cfg["messages"]["chpwd msg"]);
- return true;
- }
- $sender->sendMessage($this->cfg["messages"]["chpwd error"]);
- return false;
- break;
+ return $this->chpwd($sender, implode(" ", $args));
case "resetpwd":
- foreach($args as $name){
- $player = $this->getServer()->getOfflinePlayer($name);
- if($this->auth->unregisterPlayer($player)){
- $sender->sendMessage(TextFormat::GREEN . "$name unregistered");
- if($player instanceof Player){
- $player->sendMessage(TextFormat::YELLOW."You are no longer registered!");
- $this->auth->deauthenticatePlayer($player);
- }
- }else{
- $sender->sendMessage(TextFormat::RED . "Unable to unregister $name");
- }
- return true;
- }
- break;
+ if (count($args) != 1) return false;
+ return $this->resetpwd($sender, $args[0]);
+ case "logout":
+ if (count($args) != 0) return false;
+ return $this->logout($sender);
+ case "preregister":
+ if (count($args) != 2) return false;
+ return $this->prereg($sender,$args[0],$args[1]);
}
return false;
}
diff --git a/src/aliuly/helper/PermsHacker.php b/src/aliuly/helper/PermsHacker.php
new file mode 100644
index 0000000..ec157e3
--- /dev/null
+++ b/src/aliuly/helper/PermsHacker.php
@@ -0,0 +1,61 @@
+helper = $plugin;
+ $plugin->getServer()->getPluginManager()->registerEvents($this, $plugin);
+ $this->opts = [
+ "login" => $login,
+ "register" => $register,
+ ];
+ }
+ private function checkPerm(Player $pl, $perm) {
+ if ($pl->hasPermission($perm)) return;
+ $n = strtolower($pl->getName());
+ $this->helper->getLogger()->warning(mc::_("Fixing %1% for %2%", $perm, $n));
+ if (!isset($this->perms[$n])) $this->perms[$n] = $pl->addAttachment($this->helper);
+ $this->perms[$n]->setPermission($perm,true);
+ $pl->recalculatePermissions();
+ }
+ public function forcePerms(Player $player) {
+ if ($this->helper->auth->isPlayerAuthenticated($player)) {
+ $this->resetPerms($player);
+ return;
+ }
+ if ($this->opts["register"] && !$this->helper->auth->isPlayerRegistered($player)) {
+ $this->checkPerm($player,"simpleauth.command.register");
+ return;
+ }
+ if ($this->opts["login"]) $this->checkPerm($player,"simpleauth.command.login");
+ }
+ public function resetPerms(Player $pl) {
+ $n = strtolower($pl->getName());
+ if (isset($this->perms[$n])) {
+ $attach = $this->perms[$n];
+ unset($this->perms[$n]);
+ $pl->removeAttachment($attach);
+ $pl->recalculatePermissions();
+ }
+ }
+ public function onQuit(PlayerQuitEvent $ev) {
+ $this->resetPerms($ev->getPlayer());
+ }
+ public function onCmd(PlayerCommandPreprocessEvent $ev) {
+ $this->forcePerms($ev->getPlayer());
+ }
+}
diff --git a/src/aliuly/helper/common/MPMU.php b/src/aliuly/helper/common/MPMU.php
new file mode 100644
index 0000000..a624e69
--- /dev/null
+++ b/src/aliuly/helper/common/MPMU.php
@@ -0,0 +1,246 @@
+=, <=, <> or !=, =, !|~, <, >
+ *
+ * @param str api Installed API version
+ * @param str version API version to compare against
+ *
+ * @return bool
+ */
+ static public function apiCheck($api,$version) {
+ switch (substr($version,0,2)) {
+ case ">=":
+ return version_compare($api,trim(substr($version,2))) >= 0;
+ case "<=":
+ return version_compare($api,trim(substr($version,2))) <= 0;
+ case "<>":
+ case "!=":
+ return version_compare($api,trim(substr($version,2))) != 0;
+ }
+ switch (substr($version,0,1)) {
+ case "=":
+ return version_compare($api,trim(substr($version,1))) == 0;
+ case "!":
+ case "~":
+ return version_compare($api,trim(substr($version,1))) != 0;
+ case "<":
+ return version_compare($api,trim(substr($version,1))) < 0;
+ case ">":
+ return version_compare($api,trim(substr($version,1))) > 0;
+ }
+ if (intval($api) != intval($version)) return 0;
+ return version_compare($api,$version) >= 0;
+ }
+ /**
+ * Returns a localized string for the gamemode
+ *
+ * @param int mode
+ * @return str
+ */
+ static public function gamemodeStr($mode) {
+ if (class_exists(__NAMESPACE__."\\mc",false)) {
+ switch ($mode) {
+ case 0: return mc::_("Survival");
+ case 1: return mc::_("Creative");
+ case 2: return mc::_("Adventure");
+ case 3: return mc::_("Spectator");
+ }
+ return mc::_("%1%-mode",$mode);
+ }
+ switch ($mode) {
+ case 0: return "Survival";
+ case 1: return "Creative";
+ case 2: return "Adventure";
+ case 3: return "Spectator";
+ }
+ return "$mode-mode";
+ }
+ /**
+ * Check's player or sender's permissions and shows a message if appropriate
+ *
+ * @param CommandSender $sender
+ * @param str $permission
+ * @param bool $msg If false, no message is shown
+ * @return bool
+ */
+ static public function access(CommandSender $sender, $permission,$msg=true) {
+ if($sender->hasPermission($permission)) return true;
+ if ($msg)
+ $sender->sendMessage(mc::_("You do not have permission to do that."));
+ return false;
+ }
+ /**
+ * Check's if $sender is a player in game
+ *
+ * @param CommandSender $sender
+ * @param bool $msg If false, no message is shown
+ * @return bool
+ */
+ static public function inGame(CommandSender $sender,$msg = true) {
+ if (!($sender instanceof Player)) {
+ if ($msg) $sender->sendMessage(mc::_("You can only do this in-game"));
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Takes a player and creates a string suitable for indexing
+ *
+ * @param Player|str $player - Player to index
+ * @return str
+ */
+ static public function iName($player) {
+ if ($player instanceof Player) {
+ $player = strtolower($player->getName());
+ }
+ return $player;
+ }
+ /**
+ * Lile file_get_contents but for a Plugin resource
+ *
+ * @param Plugin $plugin
+ * @param str $filename
+ * @return str|null
+ */
+ static public function getResourceContents($plugin,$filename) {
+ $fp = $plugin->getResource($filename);
+ if($fp === null){
+ return null;
+ }
+ $contents = stream_get_contents($fp);
+ fclose($fp);
+ return $contents;
+ }
+ /**
+ * Call a plugin's function
+ *
+ * @param Server $server - pocketmine server instance
+ * @param str $plug - plugin to call
+ * @param str $method - method to call
+ * @param mixed $default - If the plugin does not exist or it is not enable, this value uis returned
+ * @return mixed
+ */
+ static public function callPlugin($server,$plug,$method,$args,$default = null) {
+ if (($plugin = $server->getPluginManager()->getPlugin($plug)) !== null
+ && $plugin->isEnabled()) {
+ $fn = [ $plugin, $method ];
+ return $fn(...$args);
+ }
+ return $default;
+ }
+ /**
+ * Register a command
+ *
+ * @param Plugin $plugin - plugin that "owns" the command
+ * @param CommandExecutor $executor - object that will be called onCommand
+ * @param str $cmd - Command name
+ * @param array $yaml - Additional settings for this command.
+ */
+ static public function addCommand($plugin, $executor, $cmd, $yaml) {
+ $newCmd = new PluginCommand($cmd,$plugin);
+ if (isset($yaml["description"]))
+ $newCmd->setDescription($yaml["description"]);
+ if (isset($yaml["usage"]))
+ $newCmd->setUsage($yaml["usage"]);
+ if(isset($yaml["aliases"]) and is_array($yaml["aliases"])) {
+ $aliasList = [];
+ foreach($yaml["aliases"] as $alias) {
+ if(strpos($alias,":")!== false) {
+ $this->owner->getLogger()->info("Unable to load alias $alias");
+ continue;
+ }
+ $aliasList[] = $alias;
+ }
+ $newCmd->setAliases($aliasList);
+ }
+ if(isset($yaml["permission"]))
+ $newCmd->setPermission($yaml["permission"]);
+ if(isset($yaml["permission-message"]))
+ $newCmd->setPermissionMessage($yaml["permission-message"]);
+ $newCmd->setExecutor($executor);
+ $cmdMap = $plugin->getServer()->getCommandMap();
+ $cmdMap->register($plugin->getDescription()->getName(),$newCmd);
+ }
+ /**
+ * Unregisters a command
+ * @param Server|Plugin $obj - Access path to server instance
+ * @param str $cmd - Command name to remove
+ */
+ static public function rmCommand($srv, $cmd) {
+ $cmdMap = $srv->getCommandMap();
+ $oldCmd = $cmdMap->getCommand($cmd);
+ if ($oldCmd === null) return false;
+ $oldCmd->setLabel($cmd."_disabled");
+ $oldCmd->unregister($cmdMap);
+ return true;
+ }
+ /**
+ * Send a PopUp, but takes care of checking if there are some
+ * plugins that might cause issues.
+ *
+ * Currently only supports SimpleAuth and BasicHUD.
+ *
+ * @param Player $player
+ * @param str $msg
+ */
+ static public function sendPopup($player,$msg) {
+ $pm = $player->getServer()->getPluginManager();
+ if (($sa = $pm->getPlugin("SimpleAuth")) !== null) {
+ // SimpleAuth also has a HUD when not logged in...
+ if ($sa->isEnabled() && !$sa->isPlayerAuthenticated($player)) return;
+ }
+ if (($hud = $pm->getPlugin("BasicHUD")) !== null) {
+ // Send pop-ups through BasicHUD
+ $hud->sendPopup($player,$msg);
+ return;
+ }
+ $player->sendPopup($msg);
+ }
+
+
+}
diff --git a/src/aliuly/helper/PluginCallbackTask.php b/src/aliuly/helper/common/PluginCallbackTask.php
similarity index 51%
rename from src/aliuly/helper/PluginCallbackTask.php
rename to src/aliuly/helper/common/PluginCallbackTask.php
index b4b4100..672cf25 100644
--- a/src/aliuly/helper/PluginCallbackTask.php
+++ b/src/aliuly/helper/common/PluginCallbackTask.php
@@ -1,32 +1,20 @@
args = $args;
$this->args[] = $this;
}
-
/**
* @return callable
*/
diff --git a/src/aliuly/helper/common/mc.php b/src/aliuly/helper/common/mc.php
new file mode 100644
index 0000000..4e66199
--- /dev/null
+++ b/src/aliuly/helper/common/mc.php
@@ -0,0 +1,106 @@
+getFile());
+ * * mc::_("string to translate\n")
+ * * mc::_("string to translate %1% %2%\n",$arg1,$arg2)
+ * * mc::n(mc::\_("singular form"),mc::\_("Plural form"),$count)
+ */
+abstract class mc {
+ /** @var str[] $txt Message translations */
+ public static $txt = [];
+ /** Main translation function
+ *
+ * This translates strings. The naming of "_" is to make it compatible
+ * with gettext utilities. The string can contain "%1%", "%2%, etc...
+ * These are inserted from the following arguments. Use "%%" to insert
+ * a single "%".
+ *
+ * @param str[] $args - messages
+ * @return str translated string
+ */
+ public static function _(...$args) {
+ $fmt = array_shift($args);
+ if (isset(self::$txt[$fmt])) $fmt = self::$txt[$fmt];
+ if (count($args)) {
+ $vars = [ "%%" => "%" ];
+ $i = 1;
+ foreach ($args as $j) {
+ $vars["%$i%"] = $j;
+ ++$i;
+ }
+ $fmt = strtr($fmt,$vars);
+ }
+ return $fmt;
+ }
+ /**
+ * Plural and singular forms.
+ *
+ * @param str $a - Singular form
+ * @param str $b - Plural form
+ * @param int $c - the number to test to select between $a or $b
+ * @return str - Either plural or singular forms depending on the value of $c
+ */
+ public static function n($a,$b,$c) {
+ return $c == 1 ? $a : $b;
+ }
+ /**
+ * Load a message file for a PocketMine plugin. Only uses .ini files.
+ *
+ * @param Plugin $plugin - owning plugin
+ * @param str $path - output of $plugin->getFile()
+ * @return int|false - false on error or the number of messages loaded
+ */
+ public static function plugin_init($plugin,$path) {
+ if (file_exists($plugin->getDataFolder()."messages.ini")) {
+ return self::load($plugin->getDataFolder()."messages.ini");
+ }
+ $msgs = $path."resources/messages/".
+ $plugin->getServer()->getProperty("settings.language").
+ ".ini";
+ if (!file_exists($msgs)) return false;
+ return mc::load($msgs);
+ }
+ /**
+ * Load the specified message catalogue.
+ * Can read .ini or .po files.
+ * @param str $f - Filename to load
+ * @return int|false - returns the number of strings loaded or false on error
+ */
+ public static function load($f) {
+ $potxt = "\n".file_get_contents($f)."\n";
+ if (preg_match('/\nmsgid\s/',$potxt)) {
+ $potxt = preg_replace('/\\\\n"\n"/',"\\n",
+ preg_replace('/\s+""\s*\n\s*"/'," \"",
+ $potxt));
+ }
+ foreach (['/\nmsgid "(.+)"\nmsgstr "(.+)"\n/',
+ '/^\s*"(.+)"\s*=\s*"(.+)"\s*$/m'] as $re) {
+ $c = preg_match_all($re,$potxt,$mm);
+ if ($c) {
+ for ($i=0;$i<$c;++$i) {
+ if ($mm[2][$i] == "") continue;
+ eval('$a = "'.$mm[1][$i].'";');
+ eval('$b = "'.$mm[2][$i].'";');
+ mc::$txt[$a] = $b;
+ }
+ return $c;
+ }
+ }
+ return false;
+ }
+}