Skip to content

Commit

Permalink
Fixed plugin loader. Thanks Wolvereness!
Browse files Browse the repository at this point in the history
  • Loading branch information
EvilSeph committed Jan 25, 2012
1 parent e2d2767 commit 0a0fee8
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 107 deletions.
12 changes: 12 additions & 0 deletions src/main/java/org/bukkit/plugin/PluginLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public interface PluginLoader {
/**
* Loads the plugin contained in the specified file
*
* @deprecated SoftDependencies are always ignored
* @param file File to attempt to load
* @param ignoreSoftDependencies Loader will ignore soft dependencies if this flag is set to true
* @return Plugin that was contained in the specified file, or null if
Expand All @@ -37,8 +38,19 @@ public interface PluginLoader {
* @throws InvalidDescriptionException If the plugin description file was invalid
* @throws UnknownDependencyException If a required dependency could not be found
*/
@Deprecated
public Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException;

/**
* Loads a PluginDescriptionFile from the specified file
*
* @param file File to attempt to load from
* @return A new PluginDescriptionFile loaded from the plugin.yml in the specified file
* @throws InvalidPluginException If when the specified file does not contain a plugin description file
* @throws InvalidDescriptionException If the plugin description file could not be created
*/
public PluginDescriptionFile getPluginDescription(File file) throws InvalidPluginException, InvalidDescriptionException;

/**
* Returns a list of all filename filters expected by this PluginLoader
*
Expand Down
242 changes: 184 additions & 58 deletions src/main/java/org/bukkit/plugin/SimplePluginManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.PluginCommandYamlParser;
Expand Down Expand Up @@ -85,54 +86,170 @@ public void registerInterface(Class<? extends PluginLoader> loader) throws Illeg
* @return A list of all plugins loaded
*/
public Plugin[] loadPlugins(File directory) {
List<Plugin> result = new ArrayList<Plugin>();
File[] files = directory.listFiles();

boolean allFailed = false;
boolean finalPass = false;
Validate.notNull(directory, "Directory cannot be null");
Validate.isTrue(directory.isDirectory(), "Directory must be a directory");

LinkedList<File> filesList = new LinkedList<File>(Arrays.asList(files));
List<Plugin> result = new ArrayList<Plugin>();
Set<Pattern> filters = fileAssociations.keySet();

if (!(server.getUpdateFolder().equals(""))) {
updateDirectory = new File(directory, server.getUpdateFolder());
}

while (!allFailed || finalPass) {
allFailed = true;
Iterator<File> itr = filesList.iterator();
Map<String, File> plugins = new HashMap<String, File>();
Set<String> loadedPlugins = new HashSet<String>();
Map<String, Collection<String>> dependencies = new HashMap<String, Collection<String>>();
Map<String, Collection<String>> softDependencies = new HashMap<String, Collection<String>>();

while (itr.hasNext()) {
File file = itr.next();
Plugin plugin = null;
// This is where it figures out all possible plugins
for (File file : directory.listFiles()) {
PluginLoader loader = null;
for (Pattern filter : filters) {
Matcher match = filter.matcher(file.getName());
if (match.find()) {
loader = fileAssociations.get(filter);
}
}

try {
plugin = loadPlugin(file, finalPass);
} catch (UnknownDependencyException ex) {
if (finalPass) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
itr.remove();
} else {
plugin = null;
if (loader == null) continue;

PluginDescriptionFile description = null;
try {
description = loader.getPluginDescription(file);
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
continue;
} catch (InvalidDescriptionException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
continue;
}

plugins.put(description.getName(), file);

Collection<? extends String> softDependencySet = (Collection<? extends String>) description.getSoftDepend();
if (softDependencySet != null) {
softDependencies.put(description.getName(), new LinkedList<String>(softDependencySet));
}

Collection<? extends String> dependencySet = (Collection<? extends String>) description.getDepend();
if (dependencySet != null) {
dependencies.put(description.getName(), new LinkedList<String>(dependencySet));
}
}

while (!plugins.isEmpty()) {
boolean missingDependency = true;
Iterator<String> pluginIterator = plugins.keySet().iterator();

while (pluginIterator.hasNext()) {
String plugin = pluginIterator.next();

if (dependencies.containsKey(plugin)) {
Iterator<String> dependencyIterator = dependencies.get(plugin).iterator();

while (dependencyIterator.hasNext()) {
String dependency = dependencyIterator.next();

// Dependency loaded
if (loadedPlugins.contains(dependency)) {
dependencyIterator.remove();

// We have a dependency not found
} else if (!plugins.containsKey(dependency)) {
missingDependency = false;
File file = plugins.get(plugin);
pluginIterator.remove();
softDependencies.remove(plugin);
dependencies.remove(plugin);

server.getLogger().log(
Level.SEVERE,
"Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ",
new UnknownDependencyException(dependency));
break;
}
}

if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) {
dependencies.remove(plugin);
}
}
if (softDependencies.containsKey(plugin)) {
Iterator<String> softDependencyIterator = softDependencies.get(plugin).iterator();

while (softDependencyIterator.hasNext()) {
String softDependency = softDependencyIterator.next();

// Soft depend is no longer around
if (!plugins.containsKey(softDependency)) {
softDependencyIterator.remove();
}
}

if (softDependencies.get(plugin).isEmpty()) {
softDependencies.remove(plugin);
}
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", ex.getCause());
itr.remove();
} catch (InvalidDescriptionException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
itr.remove();
}
if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) {
// We're clear to load, no more soft or hard dependencies left
File file = plugins.get(plugin);
pluginIterator.remove();
missingDependency = false;

if (plugin != null) {
result.add(plugin);
allFailed = false;
finalPass = false;
itr.remove();
try {
result.add(loadPlugin(file));
loadedPlugins.add(plugin);
continue;
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", ex.getCause());
} catch (InvalidDescriptionException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
} catch (UnknownDependencyException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
}
}
}
if (finalPass) {
break;
} else if (allFailed) {
finalPass = true;

if (missingDependency) {
// We now iterate over plugins until something loads
// This loop will ignore soft dependencies
pluginIterator = plugins.keySet().iterator();

while (pluginIterator.hasNext()) {
String plugin = pluginIterator.next();

if (!dependencies.containsKey(plugin)) {
softDependencies.remove(plugin);
dependencies.remove(plugin);
missingDependency = false;
File file = plugins.get(plugin);
pluginIterator.remove();

try {
result.add(loadPlugin(file));
loadedPlugins.add(plugin);
break;
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': ", ex.getCause());
} catch (InvalidDescriptionException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
} catch (UnknownDependencyException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': " + ex.getMessage(), ex);
}
}
}
// We have no plugins left without a depend
if (missingDependency) {
softDependencies.clear();
dependencies.clear();
Iterator<File> failedPluginIterator = plugins.values().iterator();

while (failedPluginIterator.hasNext()) {
File file = failedPluginIterator.next();
failedPluginIterator.remove();
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected");
}
}
}
}

Expand All @@ -151,29 +268,9 @@ public Plugin[] loadPlugins(File directory) {
* @throws UnknownDependencyException If a required dependency could not be found
*/
public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException {
return loadPlugin(file, true);
}
Validate.notNull(file, "File cannot be null");

/**
* Loads the plugin in the specified file
* <p />
* File must be valid according to the current enabled Plugin interfaces
*
* @param file File containing the plugin to load
* @param ignoreSoftDependencies Loader will ignore soft dependencies if this flag is set to true
* @return The Plugin loaded, or null if it was invalid
* @throws InvalidPluginException Thrown when the specified file is not a valid plugin
* @throws InvalidDescriptionException Thrown when the specified file contains an invalid description
* @throws UnknownDependencyException If a required dependency could not be found
*/
public synchronized Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException {
File updateFile = null;

if (updateDirectory != null && updateDirectory.isDirectory() && (updateFile = new File(updateDirectory, file.getName())).isFile()) {
if (FileUtil.copy(updateFile, file)) {
updateFile.delete();
}
}
checkUpdate(file);

Set<Pattern> filters = fileAssociations.keySet();
Plugin result = null;
Expand All @@ -185,7 +282,7 @@ public synchronized Plugin loadPlugin(File file, boolean ignoreSoftDependencies)
if (match.find()) {
PluginLoader loader = fileAssociations.get(filter);

result = loader.loadPlugin(file, ignoreSoftDependencies);
result = loader.loadPlugin(file);
}
}

Expand All @@ -197,6 +294,35 @@ public synchronized Plugin loadPlugin(File file, boolean ignoreSoftDependencies)
return result;
}

private void checkUpdate(File file) {
if (updateDirectory == null || !updateDirectory.isDirectory()) {
return;
}

File updateFile = new File(updateDirectory, file.getName());
if (updateFile.isFile() && FileUtil.copy(updateFile, file)) {
updateFile.delete();
}
}

/**
* Loads the plugin in the specified file
* <p />
* File must be valid according to the current enabled Plugin interfaces
*
* @deprecated soft-dependencies are now ignored
* @param file File containing the plugin to load
* @param ignoreSoftDependencies Loader will ignore soft dependencies if this flag is set to true
* @return The Plugin loaded, or null if it was invalid
* @throws InvalidPluginException Thrown when the specified file is not a valid plugin
* @throws InvalidDescriptionException Thrown when the specified file contains an invalid description
* @throws UnknownDependencyException If a required dependency could not be found
*/
@Deprecated
public synchronized Plugin loadPlugin(File file, boolean ignoreSoftDependencies) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException {
return loadPlugin(file);
}

/**
* Checks if the given plugin is loaded and returns it when applicable
* <p />
Expand Down
Loading

16 comments on commit 0a0fee8

@JeBobs
Copy link

@JeBobs JeBobs commented on 0a0fee8 Sep 9, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(To Wolvereness) I know this is probably not the place to post this but Dude, chill out. Look at what you are taking down because of your FLIMSY code being moved to Mojang. Take down the copyright (ik, sounds bad) because TRUST ME I know Mojang enough that I would know that your code would be replaced if you did not want it in there. facepalms so hard cracks head open

@oberhamsi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JeBobs i tried to understand the situation from reading DMCA + pull request but can't follow. would you care to elaborate why this particular commit is so problematic? is it because this is where they link to proprietary code but the result (has always been) licensed as GPL? and what do you mean with " because of your FLIMSY code being moved to Mojang"?

@KillerKozmo
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really!!!!!!!!!!!!

From: Wesley Wolfe
Date: 3 September 2014 03:48
Subject: [SL] [abuse-google] Copyright

My name is Wesley Wolfe. A site hosted on an IP address owned by Multiplay
(ip redacted) is infringing on my software copyright by the distribution
of a software known as CraftBukkit.

Original content can be found at

https://github.com/Wolvereness/Bukkit-Bleeding/commits?author=Wolvereness

0a0fee8

and the appropriate license for previously mentioned content can be found at

https://github.com/Wolvereness/Bukkit-Bleeding/blob/f210234e59275330f83b994e199c76f6abd41ee7/LICENCE.txt

The provided license requires the use of included or linking code to
provide the original source under the GNU GPL license version 3, or any
later version. An official notice has been sent to Mojang AB, whereas the
Chief Operating Officer, Vu Bui, responded with the clear text:

Mojang has not authorized the inclusion of any of its proprietary
Minecraft software (including its Minecraft Server software) within the
Bukkit project to be included in or made subject to any GPL or LGPL
license, or indeed any other open source license

As the Minecraft Server software is included in CraftBukkit, and the
original code has not been provided or its use authorized, this is a
violation of my copyright. I have a good faith belief the distribution of
CraftBukkit includes content of which the distribution is not authorized by
the copyright owner, it's agent, or the law.

Pages including infringing content:
http://dl.bukkit.org/ - links to:
http://dl.bukkit.org/latest-rb/craftbukkit.jar
http://dl.bukkit.org/latest-beta/craftbukkit-beta.jar
http://dl.bukkit.org/downloads/craftbukkit/ - links to 45 pages, with
infringing content beginning on craftbukkit build #1597 and currently
ending at craftbukkit build #3115, but please allow me not to enumerate
them all
http://dl.bukkit.org/downloads/craftbukkit/view/00703_1.1-R1/ (the
first infringing build)

I have a good faith belief that the above information is accurate and that
I am the copyright owner or authorized to act on the copyright owner's
behalf.

Pursuant to the Digital Millennium Copyright Act (17 U.S.C. § 512(c)), the
above mentioned pages need to be expeditiously removed or access-of
disabled.

Electronically Signed:

Wesley Wolfe
address redacted

email redacted

You choose to do this to bukkit? They never did anything to you what so ever! They didn't f-ing copy your code. Thanks to you bukkit is ruined. You had no right to claim something like this because contacting Bukkit. You had no proof whatsoever. You should have shame. But guess what just because they quit doesn't mean CraftBukkit is over -.-. Mojang is continuing it so your little stupid plan to get Bukkit shut down isn't really working out so well. -KillerKozmo

@thaellin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel badly for Mr Wolfe, since he authored this commit over two years ago. I believe this fact could easily be used in a court of law to show that Mr Wolfe is NOT in fact issuing this DMCA takedown request "in good faith". As such, he may be in a position to be taken to court for malicious misuse of the legal process. Additionally, if Mojang were to quantify any loss of sales they may experience as a result of this request, Mr Wolfe may be liable for the amount in question.

I am not a lawyer, but I do hope Mr Wolfe secured legal counsel and took the appropriate preliminary measures before firing off the big guns. I'm not familiar with DMCA takedown requests, (un?)fortunately, but I doubt he can just make it go away.

I'm also uncertain if Mr Wolfe has any employment position in which he would have enjoyed similar opportunity to leverage his internal knowledge to effect a takedown of a useful product; however, I am quite certain that this event would make his potential employment at most companies a non-starter.

The action was not heroic, whistleblowing, or even the act of a good samaritan; it seems more likely to have been a childish tantrum which will have negative short term consequences for the community and negative long term consequences for Mr Wolfe.

I doubt Mr Wolfe is reading this, but the term that comes to mind is 'own goal' sir.

@CrimsonDream
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a highly predictable breakdown of a relationship that used to suit both parties, and then something happened that changed everything, and uncomfortability seeped into the cracks around pride. Change always has an effect on everything it touches; and whilst some touches are daggerlike prods made with sharp fingernails and fiery breath, some are staining brushes that splashed unintendedly on both minds and egos.

But Mojang and CB need relationship counselling that's all, someone to step in as a third party and project a working compromise. Believing that much is enough because there's always a middle way; especially when children are considered ;) Will things ever be the same? No. But a brighter future is there if it is noticed, it shines outwardly from a courage found dripping from the cold showers of all egos concerned. The alternative is more tepid, and growingly resentful.

@mrmix57
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gee thanks for having bukkit.org stop giving tons of people CraftBukkit. I'm sure your copyright is having such a huge positive effect on people who actually frequently visited Bukkit.org for the program AND plugins that work with the program. Since this is text on a site found on the Internet you probably aren't able to pick up on my sarcasm for I actually hate you a lot right now)

@dsonbill
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrmix57 I honestly believe CraftBukkit and the other Bukkit derivatives will be back up eventually. @thaellin is more than likely correct in assuming an ill fate for Mr. Wolfe. Not only in the intention, of course: Mr. Wolfe sent the DMCA for content owned by Microsoft, who he was (almost certainly) not authorized to act on behalf of. Additionally, as there has been a VERY noticeable diminishment of the Minecraft brand (not that it has had an impact, but the brand itself as well as brand trust has taken a hit recently), it may be seen as... something I forget the word for. In any case, he will likely face perjury and/or fraud charges.

@scolton99
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is very frustrating to me that you would decide to file this complaint. You have cut off so many people from downloading Bukkit. As a developer, I understand your frustration but there were so many other ways to go about this.

@beast5888
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One Word he is such a bitch for doing this all he wanted was to get attention

@BringBukkitBack
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm excuse me Mr. Wolfe... I was told (by you) that the original software is available here. Might I ask where it is, or am I going to have to figure that out by myself? At least dl.bukkit.org, had a "Download" button.

@JoshCode
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I'm missing something...
Where does Wolfe actually prove that the allegedly copied code is his?
He links us this commit and he links the commits he authored, but none of the commits he authored contain the code that EvilSeph allegedly copied here.

Can somebody explain what I'm not seeing here, because I now I am puzzled as to why the DMCA takedown was accepted and executed, because I cannot find any evidence that this allegedly copied code was originally authored by Wolfe.

@BringBukkitBack
Copy link

@BringBukkitBack BringBukkitBack commented on 0a0fee8 Dec 25, 2014 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clarissa-au
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that you didn't issue the DMCA notification "in good faith" AND I really think that he did that in personal purposes,on three statements:
1)This commit is committed 3 years ago.The whole project is updated(the last)2 years ago(2013-2015).I can't agree on you that you are issuing this on good faith.
2)Are you sure that you are posting the "correct" copyrights?You should use this one:
http://creativecommons.org/licenses/by-nc-nd/4.0/
(Read about licensing at:http://choosealicense.com/licenses/)
3)Last thing to mention:If you can raise a case on Bukkit on it's license(GPL),the previous two can also raise a case on you if they want!
-> means forked in the follows:
Wolvereness/Bukkit-Bleeding->Bukkit/Bukkit-Bleeding->Bukkit/Bukkit
So,I posted it on good faith,that you issued the DMCA notice on personal purposes,that we don't know and ever not know.

@battlestarsc2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I still have no Idea what is going on. I don't really see a whole lot copied, and if so, you still don't really have a good case, and this is a problem easily fixed. I don't even know what grounds and reasons you had for reporting this to Mojang.

@fynntimes
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bukkit would probably still be alive and have found a workaround like Spigot has, if the whole staff team hadn't LEFT.

@quiquelhappy
Copy link

@quiquelhappy quiquelhappy commented on 0a0fee8 Nov 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nobody cares about bukkit anymore and its your fault. hope you are proud! thankfully spigot + paper are actively making multiplayer better for everyone. you are a disgrace to the open source community.

Please sign in to comment.