Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Suggestions ➜ temporary plugins AND/OR don't choke on 'The plugin x is already loaded.'

temporary plugins AND/OR don't choke on 'The plugin x is already loaded.'

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1 2  

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Tue 26 Apr 2011 09:03 AM (UTC)

Amended on Tue 26 Apr 2011 09:08 AM (UTC) by Fiendish

Message
So here's my back story...

I've decided that I want to make a dynamic z-ordering system for a bunch of plugins that all create miniwindows so that players can raise any miniwindow to the top of all other miniwindows (or at least the ones that subscribe to this system). In order to do this properly, I (unless I can figure out a better system) need to have a master table somewhere that keeps track of which (of 100 possible) positions are currently being used so that new plugins that get loaded that want to be in a position that is currently in use can be put into a free spot as appropriate.

But plugins can't set global world variables.
So I created a new plugin that has as its primary purpose in life, aside from containing supporting code which could have gone into a .lua file, the storage of this table. And rather than calling .lua functions directly from all the miniwindow plugins, I use CallPlugin. Ok, so far so good.

But now I have a strict dependency and I need to make sure that this monitor plugin is always loaded first, before any other plugin tries to register with it, for obvious reasons. So as a precaution I throw in:

if not IsPluginInstalled ("462b665ecb569efbf261422f") then
   LoadPlugin(GetPluginInfo(GetPluginID(),20).."aard_miniwindow_z_order_monitor.xml")

end

And that works too. Except that if a player ever goes to the Plugins... dialog and for some bonheaded-user reason reloads that plugin, when the world file saves it will have this extra bit in it that says

<include name="aard_miniwindow_z_order_monitor.xml" plugin="y" />

And THAT really messes things up, because that line will fail the next time the world is loaded, because the plugins before it have already loaded the plugin that it is trying to load. And for some reason that's a horrible catastrophe of world-ending proportions according to MUSHclient.

So for this convoluted reason, I'd like to see a possibility for flagging plugins as temporary (i.e. don't save to the world file ever) and/or make MUSHclient not choke on itself when trying to load plugins from the world file if they are already loaded.

Or let plugins set temporary global variables. Actually I'd really like that as well. I'm not a fan of brick walls in the code that I'm trying to interface with.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #1 on Tue 26 Apr 2011 09:41 AM (UTC)

Amended on Tue 26 Apr 2011 09:47 AM (UTC) by Twisol

Message
I wrote a library called PPI that lets you handle plugin dependencies pretty well. I use it for plugins that use the GMCP helper plugin, for example. It uses OnPluginListChanged to see when a plugin's been added, removed, or reloaded, and executes a callback with a proxy object so you can communicate with a plugin.

I personally don't like LoadPlugin() because it causes race conditions (so to speak) and whatnot if you have multiple plugins using it. Just add the plugin once to the plugins list. I've never had anyone tell me they had a problem with doing this, and certainly a lot of people seem to use and like my plugins, so...

Here's the code you'd add to a "user" plugin.
PPI.OnLoad("plugin ID here", function(plugin)
  plugin.YourMethodHere()
end)

OnPluginListChanged = function()
  PPI.Refresh()
end


In the "provider" plugin you do something like this to expose methods:
PPI.Register("YourMethodHere", function()
  -- ...
end)


You can grab it from any of my plugins (like Roomname[1]) if you want to use it.

[1] http://jonathan.com/achaea/plugins/RoomName.plugin.zip (direct link)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #2 on Tue 26 Apr 2011 07:57 PM (UTC)

Amended on Tue 26 Apr 2011 08:18 PM (UTC) by Fiendish

Message
I don't see how this mechanism will solve my original problem, which is that the very first thing my plugins need to do after loading is call a function from another plugin which means that the plugin gets loaded before the world file then tries (and fails verbosely) to load it again.

Honestly I MUSHclient should just silently ignore the attempt to load any plugin in the world file that is already loaded by the time it processes that line.

And (please please please) let me set global world variables from plugins so that I don't have to jump through these kinds of ridiculous hoops, because the restriction against it honestly doesn't make sense. The only given reason that I can see is that plugin authors *might* step on each others' variables. But plugin authors might step on each others' UIDs as well, and that doesn't seem to be a concern at all. Diligence in naming prevents all problems.

I guess maybe I could try using a text file as global variable storage instead...But what if I accidentally step on someone else's text file? Ugh.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #3 on Tue 26 Apr 2011 08:16 PM (UTC)
Message
What if you wait for OnPuginListChanged to be called before calling LoadPlugin? It's called only after all plugins have been loaded, unlike OnPluginInstall, and continues to be called as plugins are added or removed.

As for how PPI would be helpful, my point stands that I dislike LoadPlugin. Are your plugins useless without the z-order plugin, or can they continue operating, just without the handy feature? If the latter, just enable your extras when the helper plugin is loaded. If the former, use Note() or error() during OnPluginListChanged if the plugin hasn't been loaded yet, telling the user to install it.


I do agree that it would be good to ignore duplicates if the plugin has the same filename, since that means it's already loaded. But I think that simply patching this leaves you open to other problems, like what happens when that plugin is removed, or if that plugin is deleted wholesale from the disk. If you design plugin dependencies to be pessimistic ("this plugin probably isn't there, but I hope it is"), things work out much better. And that's what PPI is for.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #4 on Tue 26 Apr 2011 08:20 PM (UTC)

Amended on Tue 26 Apr 2011 08:31 PM (UTC) by Fiendish

Message
Twisol said:
But I think that simply patching this leaves you open to other problems, like what happens when that plugin is removed, or if that plugin is deleted wholesale from the disk. If you design plugin dependencies to be pessimistic ("this plugin probably isn't there, but I hope it is"), things work out much better.

If I could just set global temporary variables there wouldn't need to be any plugin dependencies. :\

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #5 on Tue 26 Apr 2011 08:31 PM (UTC)
Message
Fiendish said:
If I could just set global temporary variables there wouldn't need to be any plugin dependencies. :\

That's gross for the same reason global variables in normal programs are gross. =/

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #6 on Tue 26 Apr 2011 08:43 PM (UTC)

Amended on Tue 26 Apr 2011 09:01 PM (UTC) by Fiendish

Message
Quote:
But I think that simply patching this leaves you open to other problems, like what happens when that plugin is removed, or if that plugin is deleted wholesale from the disk
Eh, CallPlugin already has a provision for displaying an appropriate message if the called plugin is not present. I don't see how PPI helps much with that. If the plugin is just removed, the next launch of MUSHclient should solve that problem because other plugins re-load it. Except that right now it doesn't work properly because that makes MUSHclient spit out error messages that it really doesn't need to show.

For my purposes it's honestly even simpler than that. The data stored in this plugin is only used while plugins are loading. Anything that happens later is unimportant. So the player can remove it and nothing bad will happen at all. But see above.

I really am just intending to use this as a global data storage module. Because that's the only way to make this work right without N^2 message passing and array comparisons. It would be super nice if I didn't have to keep beating MUSHclient with the "let me do what I want, damnit!" stick.

Twisol said:

Fiendish said:
If I could just set global temporary variables there wouldn't need to be any plugin dependencies. :\

That's gross for the same reason global variables in normal programs are gross. =/

I disagree completely. First, global state absolutely has its place in normal programs and anyone who teaches otherwise is being harmfully dogmatic. The singleton pattern is hugely useful in practical coding. Second, I'm trying to construct something that just does not fit into the original idea of how plugins were never supposed to interact. IMO it was a foolish decision. There is no window manager here, so I don't have any other way of cleanly doing what I want to do. And your only objection is some hand-wavy "it's gross" without qualification. Well I say that having to, because I don't have any semblance of control over plugin load order, muck about with circuitous code paths all over the place that have to pass dozens of messages back and forth saying "Are you there? No? How about now? No? Damn. Now? Oh no too late! Abort!" is "gross". Having to even consider using the PPI library, even though it doesn't actually solve this problem, is "gross". Judiciously using a global array in the absence of an ability to guarantee that a certain plugin is always loaded first as a singleton seems far less gross.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #7 on Tue 26 Apr 2011 08:59 PM (UTC)

Amended on Tue 26 Apr 2011 09:03 PM (UTC) by Twisol

Message
Fiendish said:
First, global state absolutely has its place in normal programs and anyone who teaches otherwise is being harmfully dogmatic.

Of course. But it shouldn't be the first choice - there are usually better ways to design an architecture. I offered mine, and unless it's shown to be flawed, I can't personally condone global variables.

Fiendish said:
Second, I'm trying to construct something that just does not fit into the original idea of how plugins were never supposed to interact. IMO it was a foolish decision. There is no window manager here, so I don't have any other way of cleanly doing what I want to do. And your only objection is some hand-wavy "it's gross" without qualification. Well I say that having to, because I don't have any semblance of control over plugin load order, muck about with circuitous code paths all over the place that have to pass dozens of messages back and forth saying "Are you there? No? How about now? No? Damn. Now? Oh no too late! Abort!" is "gross".

Eh, no, sorry, that's not how it works. It's meant to behave similarly to MUSHclient's current event-based model. You register a listener for an event (in this case, a particular plugin being loaded), and you act when it happens. On the other plugin's side, it tells PPI what functions to expose over the interface. PPI handles everything from there and it's a relatively seamless communications layer.

Also, I said it's gross for the same reason global variables are gross. I didn't offer that as my reason, I just ditto'd the argument against global state without stating it outright. But since you asked, it's gross because it's extremely easy to change the value from a location you never anticipated. In other words, I code defensively (i.e. I make my functions local instead of global so other plugins can't call them), and global state makes it very hard to code defensively.

Fiendish said:
Having to even consider using the PPI library, even though it doesn't actually solve this problem, is "gross". Judiciously using a global array in the absence of an ability to guarantee that a certain plugin is always loaded first as a singleton seems far less gross.

My approach is founded on the idea that your helper plugin is "merely" a z-order tool, and miniwindows can work just fine without an explicit z-order. (They certainly have to date.) It should be possible to make the z-order benefit an optional feature, only enabled when the helper plugin has been loaded. This is a good thing: it's defensive coding.

[EDIT]
Fiendish said:
Eh, CallPlugin already has a provision for displaying an appropriate message if the called plugin is not present.

Meh. I prefer permission over forgiveness; that's another part of my defensive coding tactic.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #8 on Tue 26 Apr 2011 09:27 PM (UTC)

Amended on Tue 26 Apr 2011 09:39 PM (UTC) by Fiendish

Message
Twisol said:

But since you asked, it's gross because it's extremely easy to change the value from a location you never anticipated.

Other programmers changing things that you never expected is good! It's so disheartening to see people who get extremely myopic about their code like this. "My intent is the only one that matters." does nothing other than block people later on from doing more imaginative things with your code.

Quote:
In other words, I code defensively (i.e. I make my functions local instead of global so other plugins can't call them), and global state makes it very hard to code defensively.
In other words you do everything that I've always hated about dogmatic OO programmers. ;p

I loathe when a programmer decides to code against other programmers who know just as well what they're doing. I've struggled with foolishly incomplete PIMPL classes for years because someone decided that privacy is a pristine programming paradigm that one should always strive for. Only you can never realize the full potential ahead of time of what someone else later on would be able to do with your library if only you'd given them proper access. I'm trying to create something new here, and you're saying "Eh, it wasn't designed that way. Work around my dogmatism.".

Twisol said:

My approach is founded on the idea that your helper plugin is "merely" a z-order tool, and miniwindows can work just fine without an explicit z-order. (They certainly have to date.) It should be possible to make the z-order benefit an optional feature, only enabled when the helper plugin has been loaded. This is a good thing: it's defensive coding.
It's not a good thing to be forced to design around the possibility of a user doing something stupid (reloading a plugin accidentally) when you should be able to just make it so that the user can't do anything stupid unintentionally (not having to have a plugin in the first place). We're talking about plugins made by me trying to use a system made by me. It is not merely a tool. It is the framework for a window management system that really shouldn't have to be made into a plugin except for one single globally accessible array. A system that I want to build into all of my plugins. Except you're saying "but plugins don't work that way!" And I'm saying "Yes, well the current plugin boundaries are insufficient to house my creativity without a significant amount of time spent bashing against brick walls looking for a crack. But with a minor change or two it would all work just fine."

I thought the point of plugins was to save effort, not require more of it.


https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #9 on Tue 26 Apr 2011 09:38 PM (UTC)

Amended on Tue 26 Apr 2011 09:41 PM (UTC) by Twisol

Message
Fiendish said:
Other programmers changing things that you never expected is good! It's so disheartening to see people who get extremely myopic about their code like this. "My intent is the only one that matters." does nothing other than block people later on from doing more imaginative things with your code.

...no, actually, it's about consistent state. I'm one of the more vocal protesters against some of MUSHclient's API decisions for exactly the reason of "why limit it?", but I don't suggest breaking down the walls that guard the data. Data needs to stay consistent. Global data is harder to keep consistent. I could care less if someone used my data to do something I didn't intend, unless it causes inconsistencies (which usually turn out to be bugs).

Fiendish said:
In other words you do everything that I've always hated about dogmatic OO programmers. ;p

I code against bugs, not programmers. Defensive drivers drive against accidents, not other drivers. Please.

Fiendish said:
We're talking about plugins made by me trying to use a system made by me.

And I'm talking about outside influences affecting your shared state, causing your plugins to fail in unexpected ways. Global state is global, not "private to these particular plugins". And world variables are visible to the user in their variables list. They're gonna think, "wtf is this? *delete*".

Fiendish said:
I'm saying "Yes, well the current plugin boundaries are insufficient to house my creativity without a significant amount of time spent bashing against brick walls looking for a crack. But with a minor change or two it would all work just fine."

And I can appreciate that. I already voiced my support for not choking on loading the same plugin twice. I'm just trying to suggest an alternative which I truly think is better.

Also, MUSHclient is made for everyone, not just you. (This isn't an attack, it's a fact.) Nick has to keep in mind what's best for the client, not what one other user believes is best. Enabling shared global state without data control, I believe, is not something that's best for the client. A plugin can erect checks around its data to ensure consistency.

Fiendish said:
I thought the point of plugins was to save effort, not require more of it.

Do you want me to make a proof of concept using PPI? I will, I really will if I have to. :P

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Nick Gammon   Australia  (23,132 posts)  Bio   Forum Administrator
Date Reply #10 on Tue 26 Apr 2011 11:10 PM (UTC)
Message
Whoa!

Fiendish said:

But now I have a strict dependency and I need to make sure that this monitor plugin is always loaded first, before any other plugin tries to register with it, for obvious reasons.


Right, you have created a strict dependency, and now it is tripping you up. Why not have a design that doesn't require an exact loading order?

Back into the mists of time for a moment, I thought this sounded familiar. For one thing, my ATCP mapper needed the ATCP plugin before it would work. And, closer to what you are attempting, when I did a proof-of-concept of a MUD caching system, I wanted to have multiple plugins access a single lot of shared data (in-memory cache <-- from disk cache <-- server cache).

So, how did I do that without getting bogged down in arguments about OOP methods?

Well it hinges on one of Twisol's ideas - that is that the plugin needs to know when all the plugins have been loaded (eg. at world startup), or when the plugins list has changed (eg. the user adds/deletes/reloads stuff).

So in ATCP_Mapper.xml we have this:


function OnPluginListChanged ()
  do_plugin_check_now ("85f72d0e263d75df7bde6f00", "ATCP_NJG")    -- check we have ATCP plugin
end -- OnPluginListChanged


So once all plugins have been loaded, and the order doesn't matter, we now check that we have all the plugins we want.

This particular function, in checkplugin.lua does this:


function do_plugin_check_now (id, name)

  if IsPluginInstalled (id) then
    return  -- all is well
  end -- plugin is installed

  ColourNote ("white", "green", "Plugin '" .. name .. "' not installed. Attempting to install it...") 
  LoadPlugin (GetPluginInfo(GetPluginID (), 20) .. name .. ".xml") 

  if IsPluginInstalled (id) then
    ColourNote ("white", "green", "Success!") 
    return  -- all is well ... now
  end -- plugin is installed
    
  ColourNote ("white", "red", string.rep ("-", 80))
  ColourNote ("white", "red", "Plugin '" .. name .. "' not installed. Please download and install it.") 
  ColourNote ("white", "red", "It is required for the correct operation of the " ..
              GetPluginName () .. " plugin.")
  ColourNote ("white", "red", string.rep ("-", 80))

end -- do_plugin_check_now


So it tries to load the required plugin, and if that fails, then it gives up and notifies the user.

Similarly my (original) mapper does this:


function OnPluginListChanged ()
  item_ppi = load_ppi ("928dc37b201539cd14239ff0", "Item_Cache_Helper")
end -- OnPluginListChanged


This "item cache helper" is effectively your "global variables" plugin, except that they are just the variables needed for this subsystem.

If you can see a major flaw, please let me know. But basically instead of expecting to be able to do everything in the "install" part you wait for the "plugin list changed" part, with a couple of flags to let you know whether this is the first time through or not.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #11 on Wed 27 Apr 2011 06:13 AM (UTC)

Amended on Wed 27 Apr 2011 06:26 AM (UTC) by Fiendish

Message
Nick Gammon said:

Whoa!

Heh, sorry. I got a bit carried away.

Anyway, this will probably work. But it's still a bunch of code that works around the problem that MUSHclient throws an error when trying to load a plugin from the world file that is already loaded, when it seems like just skipping those and not throwing an error would be a good thing.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #12 on Thu 21 Mar 2013 06:40 AM (UTC)
Message
I just hit this issue again. And I still think that MUSHclient should silently ignore already loaded plugins.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Nick Gammon   Australia  (23,132 posts)  Bio   Forum Administrator
Date Reply #13 on Thu 21 Mar 2013 07:46 AM (UTC)
Message
Remember, plugins have a sequence parameter now.

And when you say "MUSHclient should" do you mean the Plugins dialog box, or the LoadPlugin function? Or something else?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #14 on Fri 22 Mar 2013 01:59 AM (UTC)
Message
Ah, sorry. I mean referring to this statement in my last post from 2011...
Quote:
MUSHclient throws an error when trying to load a plugin from the world file that is already loaded, when it seems like just skipping those and not throwing an error would be a good thing.

https://github.com/fiendish/aardwolfclientpackage
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


54,842 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.