Selectively loading your plugins

Wrote new code? Fixed a bug? Want to discuss technical stuff? Feel free to post it here.

Moderator: Moderators

Message
Author
Technology
Super Moderators
Super Moderators
Posts: 801
Joined: 06 May 2008, 12:47
Noob?: No

Selectively loading your plugins

#1 Post by Technology »

[EDIT]
This feature has been comitted into SVN now !
There is no need to install it yourself and if you want to use this, then read this post.
[/EDIT]

Its usefull to me, i guess it would be usefull for others aswell.
You need plugins.txt in your control folder.
I don't know what you guys think but maybe it won't be bad to put the plugin config in here aswell.
Since there are hooks even before config.txt is loaded.
Improve it if you can.
plugins.txt:

Code: Select all

# 0 : do not automatically load plugins
# 1 : automatically load all plugins
# 2 : selectively load plugins

autoLoad 2
loadPlugins macro, dKore
Diffpatch:

Code: Select all

Index: functions.pl
===================================================================
--- functions.pl	(revision 6428)
+++ functions.pl	(working copy)
@@ -107,7 +107,32 @@
 }
 
 sub loadPlugins {
+	no encoding 'utf8';
+	Settings::addControlFile('plugins.txt',		loader => [\&parseConfigFile, \%plugins]);
+	use encoding 'utf8';
 	eval {
+		my $progressHandler = sub {
+			my ($filename) = @_;
+			message TF("Loading %s...\n", $filename);
+		};
+		Settings::loadAll($progressHandler);
+	};
+	if (my $e = caught('UTF8MalformedException')) {
+		$interface->errorDialog(TF(
+			"The file %s must be valid UTF-8 encoded, which it is \n" .
+			"currently not. To solve this prolem, please use Notepad\n" .
+			"to save that file as valid UTF-8.",
+			$e->textfile));
+		exit 1;
+	} elsif (my $e = caught('FileNotFoundException')) {
+		$interface->errorDialog(TF(	"Unable to load the file %s. \n" .
+		"It should be located in %s. \n",
+		$e->filename, Settings::getControlFolders));
+		exit 1;
+	} elsif ($@) {
+		die $@;
+	}
+	eval {
 		Plugins::loadAll();
 	};
 	if (my $e = caught('Plugin::LoadException')) {
Index: Globals.pm
===================================================================
--- Globals.pm	(revision 6428)
+++ Globals.pm	(working copy)
@@ -26,7 +26,7 @@
 # Do not use any other Kore modules here. It will create circular dependancies.
 
 our %EXPORT_TAGS = (
-	config  => [qw(%arrowcraft_items %avoid @chatResponses %cities_lut %config %consoleColors %directions_lut %equipTypes_lut %equipSlot_rlut %equipSlot_lut %haircolors @headgears_lut %items_control %items_lut %itemSlotCount_lut %itemsDesc_lut %itemTypes_lut %jobs_lut %maps_lut %masterServers %monsters_lut %npcs_lut %packetDescriptions %portals_lut %responses %sex_lut %shop %skillsDesc_lut %skillsLooks %skillsArea %skillsEncore %skillsSP_lut %spells_lut %emotions_lut %timeout $char %mon_control %priority %routeWeights %pickupitems %rpackets %itemSlots_lut %skillsStatus %portals_los %skillsState %skillsAilments %elements_lut)],
+	config  => [qw(%arrowcraft_items %avoid @chatResponses %cities_lut %config %consoleColors %directions_lut %equipTypes_lut %equipSlot_rlut %equipSlot_lut %haircolors @headgears_lut %items_control %items_lut %itemSlotCount_lut %itemsDesc_lut %itemTypes_lut %jobs_lut %maps_lut %masterServers %monsters_lut %npcs_lut %packetDescriptions %plugins %portals_lut %responses %sex_lut %shop %skillsDesc_lut %skillsLooks %skillsArea %skillsEncore %skillsSP_lut %spells_lut %emotions_lut %timeout $char %mon_control %priority %routeWeights %pickupitems %rpackets %itemSlots_lut %skillsStatus %portals_los %skillsState %skillsAilments %elements_lut)],
 	ai      => [qw(@ai_seq @ai_seq_args %ai_v $AI $AI_forcedOff %targetTimeout)],
 	state   => [qw($accountID $cardMergeIndex @cardMergeItemsID $charID @chars @chars_old %cart @friendsID %friends %incomingFriend %field $field %homunculus $itemsList @itemsID %items $monstersList @monstersID %monsters @npcsID %npcs $npcsList @playersID %players @portalsID @portalsID_old %portals %portals_old $portalsList @storeList $currentChatRoom @currentChatRoomUsers @chatRoomsID %createdChatRoom %chatRooms @skillsID %storage @storageID @arrowCraftID %guild %incomingGuild @spellsID %spells @unknownPlayers @unknownNPCs $statChanged $skillChanged $useArrowCraft %currentDeal %incomingDeal %outgoingDeal @identifyID @partyUsersID %incomingParty @petsID %pets @venderItemList $venderID @venderListsID @articles $articles %venderLists %monsters_old @monstersID_old %npcs_old %items_old %players_old @playersID_old @servers $sessionID $sessionID2 $accountSex $accountSex2 $map_ip $map_port $KoreStartTime $secureLoginKey $initSync $lastConfChangeTime $petsList $playersList $portalsList @playerNameCacheIDs %playerNameCache %pet $pvp @cashList)],
 	network => [qw($remote_socket $net $messageSender $charServer $conState $conState_tries $encryptVal $ipc $bus $lastPacketTime $masterServer $lastswitch $packetParser $bytesSent $bytesReceived $incomingMessages $outgoingClientMessages $enc_val1 $enc_val2)],
@@ -103,6 +103,7 @@
 our %monsters_lut;
 our %npcs_lut;
 our %packetDescriptions;
+our %plugins;
 our %portals_los;
 our %portals_lut;
 our %priority;
Index: Plugins.pm
===================================================================
--- Plugins.pm	(revision 6428)
+++ Plugins.pm	(working copy)
@@ -34,7 +34,7 @@
 use Modules 'register';
 use Globals;
 use Utils qw(stringToQuark quarkToString);
-use Utils::DataStructures qw(binAdd);
+use Utils::DataStructures qw(binAdd existsInList);
 use Utils::ObjectList;
 use Utils::Exceptions;
 use Log qw(message);
@@ -71,6 +71,12 @@
 ##
 # void Plugins::loadAll()
 #
+# autoLoad
+#  0 : do not automatically load plugins
+#  1 : automatically load all plugins
+#  2 : selectively load plugins
+# loadPlugins pluginname1, pluginname2, ...
+#
 # Loads all plugins from the plugins folder, and all plugins that are one subfolder below
 # the plugins folder. Plugins must have the .pl extension.
 #
@@ -78,41 +84,51 @@
 # Throws Plugin::DeniedException if the plugin system refused to load a plugin. This can
 # happen, for example, if it detects that a plugin is incompatible.
 sub loadAll {
-	my (@plugins, @subdirs);
+	message TF("autoLoad is %s\n", $plugins{'autoLoad'});
+	if ($plugins{'autoLoad'}) {
+		my (@plugins, @subdirs, @files);
 
-	my @pluginsFolders;
-	@pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
-	foreach my $dir (@pluginsFolders) {
-		my @items;
+		my @pluginsFolders;
+		@pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
+		foreach my $dir (@pluginsFolders) {
+			my @items;
 
-		next if (!opendir(DIR, $dir));
-		@items = readdir DIR;
-		closedir DIR;
+			next if (!opendir(DIR, $dir));
+			@items = readdir DIR;
+			closedir DIR;
 
-		foreach my $file (@items) {
-			push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
+			foreach my $file (@items) {
+				push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
+				push @files, "$file" if ($file =~ /\.(pl|lp)$/);
+			}
+			foreach my $subdir (@items) {
+				push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
+			}
 		}
-		foreach my $subdir (@items) {
-			push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
+
+		foreach my $plugin (@plugins) {
+			my $file = substr(shift(@files), 0, - 3);
+			next if (!existsInList($plugins{loadPlugins}, $file) && ($plugins{autoLoad} == 2));
+			load($plugin);
 		}
-	}
 
-	foreach my $plugin (@plugins) {
-		load($plugin);
-	}
+		foreach my $dir (@subdirs) {
+			next unless (opendir(DIR, $dir));
+			@plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
+			foreach my $file (@plugins) {
+				push @files, "$file" if ($file =~ /\.(pl|lp)$/);
+			}
+			closedir(DIR);
 
-	foreach my $dir (@subdirs) {
-		next unless (opendir(DIR, $dir));
-		@plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
-		closedir(DIR);
-
-		foreach my $plugin (@plugins) {
-			load("$dir/$plugin");
+			foreach my $plugin (@plugins) {
+			my $file = substr(shift(@files), 0, - 3);
+				next if (!existsInList($plugins{loadPlugins}, $file) && ($plugins{autoLoad} == 2));
+				load("$dir/$plugin");
+			}
 		}
 	}
 }
 
-
 ##
 # void Plugins::load(String file)
 # file: The filename of a plugin.
Last edited by Technology on 27 Jun 2008, 05:06, edited 4 times in total.
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!

hakore
Super Moderators
Super Moderators
Posts: 200
Joined: 16 May 2008, 08:28
Noob?: No
Contact:

Re: Selectively loading your plugins

#2 Post by hakore »

Nice work. I personally think this can be committed readily.

Let me, however, review this for a while more and let other devs see it as well.

Moved.
Whatever...

h4rry84
Moderators
Moderators
Posts: 234
Joined: 04 Apr 2008, 09:30
Noob?: Yes
Location: My House
Contact:

Re: Selectively loading your plugins

#3 Post by h4rry84 »

woot, just been know this idea :D wonder nobody ever mentioned this idea before :mrgreen:

Alexander
Noob
Noob
Posts: 11
Joined: 29 May 2008, 12:00
Noob?: Yes

Re: Selectively loading your plugins

#4 Post by Alexander »

Hasn't it been in the wiki to do list for a while now?

hakore
Super Moderators
Super Moderators
Posts: 200
Joined: 16 May 2008, 08:28
Noob?: No
Contact:

Re: Selectively loading your plugins

#5 Post by hakore »

I just realized we already have an existing control file that is loaded before any other plugins: "sys.txt"

Maybe we'd rather use that instead of adding a "plugins.txt"

Reference (openkore.pl):

Code: Select all

sub __start {
	use ErrorHandler;
	use XSTools;
	srand();


	##### BASIC INITIALIZATION #####

	use Translation;
	use Settings qw(%sys);
	use Utils::Exceptions;

	eval "use OpenKoreMod;";
	undef $@;
	parseArguments();
	Settings::loadSysConfig();
Whatever...

Technology
Super Moderators
Super Moderators
Posts: 801
Joined: 06 May 2008, 12:47
Noob?: No

Re: Selectively loading your plugins

#6 Post by Technology »

I guess you could do that.
It doesn't matter much to me, it may be a bit less user friendly tho.
All that counts is its functionality imo.

Since i'm not able to make a diffpatch on this pc, i'll just post a code snippet instead. (given that Plugins.pm hasn't been changed in ages ?)
And since we are using sys.txt here, no edits have to be made in Globals.pm and functions.pl !!!
And don't forget to add autoLoad and loadPlugins to your sys.txt
untested:

Code: Select all

#########################################################################
#  OpenKore - Plugin system
#
#  This software is open source, licensed under the GNU General Public
#  License, version 2.
#  Basically, this means that you're allowed to modify and distribute
#  this software. However, if you distribute modified versions, you MUST
#  also distribute the source code.
#  See http://www.gnu.org/licenses/gpl.html for the full license.
#
#  $Revision: 5989 $
#  $Id: Plugins.pm 5989 2007-09-30 11:36:16Z vcl_kore $
#
#########################################################################
##
# MODULE DESCRIPTION: Plugin system
#
# This module provides an interface for handling plugins.
# See the <a href="http://www.openkore.com/wiki/index.php/How_to_write_plugins_for_OpenKore">Plugin
# Writing Tutorial</a> for more information about plugins.
#
# NOTE: Do not confuse plugins with modules! See Modules.pm for more information.

# TODO: use events instead of printing log information directly.

package Plugins;

use strict;
use warnings;
use Time::HiRes qw(time sleep);
use Exception::Class ('Plugin::LoadException', 'Plugin::DeniedException');
use UNIVERSAL qw(isa);

use Modules 'register';
use Globals;
use Utils qw(stringToQuark quarkToString);
use Utils::DataStructures qw(binAdd existsInList);
use Utils::ObjectList;
use Utils::Exceptions;
use Log qw(message);
use Translation qw(T TF);
use Settings qw(%sys);



#############################
### CATEGORY: Variables
#############################

##
# String $Plugins::current_plugin
#
# When a plugin is being (re)loaded, the filename of the plugin is set in this variable.
our $current_plugin;

##
# String $Plugins::current_plugin_folder
#
# When a plugin is being (re)loaded, the the plugin's folder is set in this variable.
our $current_plugin_folder;

our @plugins;
our %hooks;

use enum qw(HOOKNAME INDEX);
use enum qw(CALLBACK USER_DATA);


#############################
### CATEGORY: Functions
#############################

##
# void Plugins::loadAll()
#
# loadPlugins <0|1|2>
#	this option controls loading of plugins at startup or when the "plugin load all" command is used.
#	0 : do not load plugins
#	1 : load all plugins
#	2 : only load plugins that are listed in loadPlugins_list
# loadPlugins_list <list>
#	if loadPlugins is set to 2, this comma-separated list of plugin filenames specifies
#	which plugin files to load at startup or when the "plugin load all" command is used.
#
# Loads all plugins from the plugins folder, and all plugins that are one subfolder below
# the plugins folder. Plugins must have the .pl extension.#
# Throws Plugin::LoadException if a plugin failed to load.
# Throws Plugin::DeniedException if the plugin system refused to load a plugin. This can
# happen, for example, if it detects that a plugin is incompatible.
sub loadAll {
message TF("loadPlugins is %s\n", $sys{'loadPlugins'});
return unless ($sys{'loadPlugins'});
   my (@plugins, @subdirs, @files);

   my @pluginsFolders;
   @pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
   foreach my $dir (@pluginsFolders) {
      my @items;

      next if (!opendir(DIR, $dir));
      @items = readdir DIR;
      closedir DIR;

      foreach my $file (@items) {
         push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
         push @files, "$file" if ($file =~ /\.(pl|lp)$/);
      }
      foreach my $subdir (@items) {
         push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
      }
   }

   foreach my $plugin (@plugins) {
      my $file = substr(shift(@files), 0, - 3);
      next if (!existsInList($sys{loadPlugins_list}, $file) && ($sys{loadPlugins} == 2));
      load($plugin);
   }

   foreach my $dir (@subdirs) {
      next unless (opendir(DIR, $dir));
      @plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
foreach my $file (@plugins) {
push @files, "$file" if ($file =~ /\.(pl|lp)$/);
}
      closedir(DIR);

      foreach my $plugin (@plugins) {
         my $file = substr(shift(@files), 0, - 3);
         next if (!existsInList($sys{loadPlugins_list}, $file) && ($sys{loadPlugins} == 2));
         load("$dir/$plugin");
      }
   }
}
Below is a more userfriendly message but i am aware that the scalars in @autoLoad won't be able to be translated, maybe somebody knows a better solution ?
untested:

Code: Select all

my @loadPlugins = ("no", "all", "selectively chosen");
message TF("Loading %s plugins.\n", $loadPlugins[$sys{'loadPlugins'}]);
EDIT: changed names autoLoad to loadPlugins (see Hakore's next post)
Last edited by Technology on 26 Jun 2008, 04:47, edited 2 times in total.
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!

hakore
Super Moderators
Super Moderators
Posts: 200
Joined: 16 May 2008, 08:28
Noob?: No
Contact:

Re: Selectively loading your plugins

#7 Post by hakore »

Well, sys.txt is there for a reason. It controls Openkore's startup behavior, and your proposed "config option" for selectively loading plugins generally falls under this category.

Being a bit less user-friendly can be supplemented by good documentation.

Also, the implementation of loading the control file plugins.txt is not graceful enough since it is loaded twice during the program startup. We should avoid such unnecessary performance overhead.

Finally, since the config options for controling this feature will be placed in sys.txt, we have to rename the proposed config options. "autoLoad" will be confusing when reading sys.txt. "loadPlugins" is a self-explanatory name.

I propose therefore (sys.txt):

Code: Select all

###### Plugin settings ######

# loadPlugins <0|1|2>
#   this option controls loading of plugins at startup or when the "plugin load all" command is used.
#   0 : do not load plugins
#   1 : load all plugins
#   2 : only load plugins that are listed in loadPlugins_list
loadPlugins 1

# loadPlugins_list <list>
#   if loadPlugins is set to 2, this comma-separated list of plugin filenames specifies
#   which plugin files to load at startup or when the "plugin load all" command is used.
loadPlugins_list
I'll commit your proposal later this evening.
Whatever...

Technology
Super Moderators
Super Moderators
Posts: 801
Joined: 06 May 2008, 12:47
Noob?: No

Re: Selectively loading your plugins

#8 Post by Technology »

I agree on every statement u made here.
Also changing the name of the config options is a good idea, i will now adjust these in the code above your post.
Thanks for your involvement in this matter Hakore.
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!

hakore
Super Moderators
Super Moderators
Posts: 200
Joined: 16 May 2008, 08:28
Noob?: No
Contact:

Re: Selectively loading your plugins

#9 Post by hakore »

This will be the working patch:

I just had to clean up and optimize the Plugins::loadAll subroutine a bit (remove redundant loops and avoid redundant if statements) together with the implementation of selective plugin loading. So as you can see, this will be a bit different from your original patch. However, all the logic you proposed comprised this.

I also added a fallback logic for those who do not have loadPlugins set in sys.txt.

Code: Select all

Index: Plugins.pm
===================================================================
--- Plugins.pm	(revision 6435)
+++ Plugins.pm	(working copy)
@@ -34,11 +34,12 @@
 use Modules 'register';
 use Globals;
 use Utils qw(stringToQuark quarkToString);
-use Utils::DataStructures qw(binAdd);
+use Utils::DataStructures qw(binAdd existsInList);
 use Utils::ObjectList;
 use Utils::Exceptions;
 use Log qw(message);
 use Translation qw(T TF);
+use Settings qw(%sys);
 
 
 #############################
@@ -78,10 +79,22 @@
 # Throws Plugin::DeniedException if the plugin system refused to load a plugin. This can
 # happen, for example, if it detects that a plugin is incompatible.
 sub loadAll {
-	my (@plugins, @subdirs);
+	if (!exists $sys{'loadPlugins'}) {
+		message T("Loading all plugins (by default)...\n", 'plugins');
+	} elsif (!$sys{'loadPlugins'}) {
+		message T("Automatic loading of plugins disabled\n", 'plugins');
+		return;
+	} elsif ($sys{'loadPlugins'} eq '1') {
+		message T("Loading all plugins...\n", 'plugins');
+	} elsif ($sys{'loadPlugins'} eq '2') {
+		message T("Selectively loading plugins...\n", 'plugins');
+	}
+	
+	my (@plugins, @subdirs, @names);
 
 	my @pluginsFolders;
 	@pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
+
 	foreach my $dir (@pluginsFolders) {
 		my @items;
 
@@ -90,26 +103,38 @@
 		closedir DIR;
 
 		foreach my $file (@items) {
-			push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
+			if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/) {
+				push @plugins, "$dir/$file";
+				push @names, substr($file, 0, -3) if (exists $sys{'loadPlugins'} && $sys{'loadPlugins'} eq '2');
+			} elsif (-d "$dir/$file" && $file !~ /^(\.|CVS$)/i) {
+				push @subdirs, "$dir/$file";
+			}
 		}
-		foreach my $subdir (@items) {
-			push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
-		}
 	}
 
-	foreach my $plugin (@plugins) {
-		load($plugin);
-	}
-
 	foreach my $dir (@subdirs) {
-		next unless (opendir(DIR, $dir));
-		@plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
-		closedir(DIR);
+		my @items;
 
-		foreach my $plugin (@plugins) {
-			load("$dir/$plugin");
+		next if (!opendir(DIR, $dir));
+		@items = readdir DIR;
+		closedir DIR;
+
+		foreach my $file (@items) {
+			if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/) {
+				push @plugins, "$dir/$file";
+				push @names, substr($file, 0, -3) if (exists $sys{'loadPlugins'} && $sys{'loadPlugins'} eq '2');
+			}
 		}
 	}
+
+	while (@plugins) {
+		my $plugin = shift(@plugins);
+		if (exists $sys{'loadPlugins'} && $sys{'loadPlugins'} eq '2') {
+			my $file = shift(@names);
+			next if (exists $sys{'loadPlugins_list'} && !existsInList($sys{'loadPlugins_list'}, $file));
+		}
+		load($plugin);
+	}
 }


And the default sys.txt for the confpack.

Code: Select all

Index: sys.txt
===================================================================
--- sys.txt	(revision 6435)
+++ sys.txt	(working copy)
@@ -35,3 +35,17 @@
 
 ###### Miscellaneous ######
 sendAnonymousStatisticReport 1
+
+
+###### Plugin settings ######
+# loadPlugins <0|1|2>
+#   this option controls loading of plugins at startup or when the "plugin load all" command is used.
+#   0 : do not load plugins
+#   1 : load all plugins
+#   2 : only load plugins that are listed in loadPlugins_list
+loadPlugins 1
+
+# loadPlugins_list <list>
+#   if loadPlugins is set to 2, this comma-separated list of plugin names (filename without the extension)
+#   specifies which plugin files to load at startup or when the "plugin load all" command is used.
+loadPlugins_list macro
Whatever...

hakore
Super Moderators
Super Moderators
Posts: 200
Joined: 16 May 2008, 08:28
Noob?: No
Contact:

Re: Selectively loading your plugins

#10 Post by hakore »

Finally, committed.

Thanks Technology.
Whatever...

Post Reply