Skip to content

Commit

Permalink
Updated to fix attack damage as well as attack speed
Browse files Browse the repository at this point in the history
  • Loading branch information
rayzr522 committed Feb 24, 2017
1 parent 3168fc8 commit 8dcd58c
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 58 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# OCMFixer
Fixes the attack speed of all offline players by reading all playerdata files in a world folder. To know which world to specify, look through all your worlds for one that has a non-empty `playerdata` folder.
Fixes the attack speed of all offline players by reading and modifying all playerdata files in a world folder.

To then fix the world, just do `/read <world>` from the console or in-game. Usually the world that has the playerdata files is the one specified as the main world in your `server.properties`, so for example for my test server it was `Hub`. To fix it I just did `/read Hub`. It's as easy as that! :grin:
## Installation
You can [download the latest version from the releases page](https://www.github.com/Rayzr522/OCMFixer/releases).

[Downloads](https://www.github.com/Rayzr522/OCMFixer/releases)
## Usage
_Warning: To apply this to all players, it's suggested to do this while no players are online. The changes made by this plugin will be overwritten for any player that is online while executing the command, meaning it won't work for them. It's probably best to do this immediatly after a restart, or after doing `/kickall`._

To know which world to specify, look through all your worlds for one that has a non-empty `playerdata` folder. Then, to fix the world, just do `/read <world>` from the console (_or in-game, but again, it's best not to do this while anyone is online, including yourself_). Usually the world that has the playerdata files is the one specified as the main world in your `server.properties`. As an example, for my test server the main world was `Hub`. To fix it I just did `/read Hub`. It's as easy as that!
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.rayzr522</groupId>
<artifactId>ocmfixer</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
<name>OCMFixer</name>
<description>Fixes the attack speed of players</description>
<repositories>
Expand Down
46 changes: 23 additions & 23 deletions src/main/java/com/comphenix/attribute/NbtFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// Just some notes, I had to modify a few parts of this to make it... functional

package com.comphenix.attribute;

import java.io.BufferedInputStream;
Expand Down Expand Up @@ -59,16 +61,15 @@
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.io.ByteSink;
import com.google.common.io.Closeables;
import com.google.common.io.Files;
import com.google.common.io.OutputSupplier;
import com.google.common.primitives.Primitives;

@SuppressWarnings("deprecation")
public class NbtFactory {
// Convert between NBT id and the equivalent class in java
private static final BiMap<Integer, Class<?>> NBT_CLASS = HashBiMap.create();
private static final BiMap<Integer, NbtType> NBT_ENUM = HashBiMap.create();
private static final BiMap<Integer, NbtType> NBT_ENUM = HashBiMap.create();

/**
* Whether or not to enable stream compression.
Expand Down Expand Up @@ -114,26 +115,26 @@ else if (this == TAG_LIST)
}

// The NBT base class
private Class<?> BASE_CLASS;
private Class<?> COMPOUND_CLASS;
private Class<?> STREAM_TOOLS;
private Class<?> READ_LIMITER_CLASS;
private Method NBT_CREATE_TAG;
private Method NBT_GET_TYPE;
private Field NBT_LIST_TYPE;
private final Field[] DATA_FIELD = new Field[12];
private Class<?> BASE_CLASS;
private Class<?> COMPOUND_CLASS;
private Class<?> STREAM_TOOLS;
private Class<?> READ_LIMITER_CLASS;
private Method NBT_CREATE_TAG;
private Method NBT_GET_TYPE;
private Field NBT_LIST_TYPE;
private final Field[] DATA_FIELD = new Field[12];

// CraftItemStack
private Class<?> CRAFT_STACK;
private Field CRAFT_HANDLE;
private Field STACK_TAG;
private Class<?> CRAFT_STACK;
private Field CRAFT_HANDLE;
private Field STACK_TAG;

// Loading/saving compounds
private LoadCompoundMethod LOAD_COMPOUND;
private Method SAVE_COMPOUND;
private Method SAVE_COMPOUND;

// Shared instance
private static NbtFactory INSTANCE;
private static NbtFactory INSTANCE;

/**
* Represents a root NBT compound.
Expand Down Expand Up @@ -275,7 +276,7 @@ public <T> T getPath(String path) {
* @param option - whether or not to compress the output.
* @throws IOException If anything went wrong.
*/
public void saveTo(OutputSupplier<? extends OutputStream> stream, StreamOptions option) throws IOException {
public void saveTo(ByteSink stream, StreamOptions option) throws IOException {
saveStream(this, stream, option);
}

Expand Down Expand Up @@ -484,7 +485,6 @@ public static NbtList fromList(Object nmsList) {
* @return The decoded NBT compound.
* @throws IOException If anything went wrong.
*/
@SuppressWarnings("resource")
public static NbtCompound fromStream(InputStream stream, StreamOptions option) throws IOException {
InputStream input = null;
DataInputStream data = null;
Expand Down Expand Up @@ -518,13 +518,13 @@ else if (input != null)
* @param option - whether or not to compress the output.
* @throws IOException If anything went wrong.
*/
public static void saveStream(NbtCompound source, OutputSupplier<? extends OutputStream> stream, StreamOptions option) throws IOException {
public static void saveStream(NbtCompound source, ByteSink stream, StreamOptions option) throws IOException {
OutputStream output = null;
DataOutputStream data = null;
boolean suppress = true;

try {
output = stream.getOutput();
output = stream.openStream();
data = new DataOutputStream(
option == StreamOptions.GZIP_COMPRESSION ? new GZIPOutputStream(output) : output);

Expand Down Expand Up @@ -864,7 +864,7 @@ public Object wrap(Object value) {
* @author Kristian
*/
private class ConvertedMap extends AbstractMap<String, Object> implements Wrapper {
private final Object handle;
private final Object handle;
private final Map<String, Object> original;

private final CachedNativeWrapper cache = new CachedNativeWrapper();
Expand Down Expand Up @@ -968,9 +968,9 @@ public Object getHandle() {
* @author Kristian
*/
private class ConvertedList extends AbstractList<Object> implements Wrapper {
private final Object handle;
private final Object handle;

private final List<Object> original;
private final List<Object> original;
private final CachedNativeWrapper cache = new CachedNativeWrapper();

public ConvertedList(Object handle, List<Object> original) {
Expand Down
104 changes: 74 additions & 30 deletions src/main/java/com/rayzr522/ocmfixer/ReadCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
package com.rayzr522.ocmfixer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Optional;

import org.bukkit.ChatColor;
import org.bukkit.command.Command;
Expand All @@ -15,7 +13,7 @@
import com.comphenix.attribute.NbtFactory.NbtCompound;
import com.comphenix.attribute.NbtFactory.NbtList;
import com.comphenix.attribute.NbtFactory.StreamOptions;
import com.google.common.io.OutputSupplier;
import com.google.common.io.Files;

public class ReadCommand implements CommandExecutor {

Expand All @@ -27,22 +25,27 @@ public ReadCommand(OCMFixerPlugin plugin) {

@Override
public boolean onCommand(CommandSender p, Command command, String cmd, String[] args) {

if (args.length < 1) {
return false;
}

File folder = new File(plugin.getWorldFolder(args[0]), "playerdata");

if (!folder.exists()) {
p.sendMessage(ChatColor.RED + "That world could not be found");
if (!folder.exists() || !folder.isDirectory()) {
tell(p, "&cThat world could not be found, or it does not contain a playerdata folder.");
return true;
}

// Because I can't set variables from inside of a lambda. YAY!
ScrewYouLambdas state = new ScrewYouLambdas();

log("Beginning conversion process on world " + args[0]);
for (File file : folder.listFiles()) {

log("Attempting conversion of file `" + file.getName() + "`");
state.reset();

log("Attempting conversion of file '" + file.getName() + "'");

try {
NbtCompound nbt = NbtReader.read(file);

Expand All @@ -52,48 +55,89 @@ public boolean onCommand(CommandSender p, Command command, String cmd, String[]
}
log("Found attributes list");

NbtCompound attribute = list.stream()
.filter(it -> it instanceof NbtCompound)
.map(it -> (NbtCompound) it)
.filter(it -> it.getString("Name", "ERR").equals("generic.attackSpeed"))
.findFirst().orElse(null);
if (attribute == null) {
continue;
}
log("Found attack speed attribute");
// Reset attack speed
findByName(list, "generic.attackSpeed").ifPresent(attribute -> {
if (attribute.getDouble("Base", 0.0) == 4.0) {
return;
}

list.remove(attribute);
log("Resetting attack speed to default (4.0)");

attribute.putPath("Base", 4);
list.remove(attribute);
attribute.putPath("Base", 4.0);
list.add(attribute);

list.add(attribute);
state.markDirty();
});

nbt.putPath("Attributes", list);
// Reset attack damage
findByName(list, "generic.attackDamage").ifPresent(attribute -> {
if (attribute.getDouble("Base", 0.0) == 2.0) {
return;
}

log("Set attack speed to old value");
log("Resetting attack damage to default (2.0)");

NbtFactory.saveStream(nbt, new OutputSupplier<OutputStream>() {
@Override
public OutputStream getOutput() throws IOException {
return new FileOutputStream(file);
}
}, StreamOptions.GZIP_COMPRESSION);
list.remove(attribute);
attribute.putPath("Base", 2.0);
list.add(attribute);

state.markDirty();
});

if (state.isDirty()) {
nbt.putPath("Attributes", list);
log("Saving modified data...");

NbtFactory.saveStream(nbt, Files.asByteSink(file), StreamOptions.GZIP_COMPRESSION);

log("Done. Data reset for '" + file.getName() + "'");
} else {
log("'" + file.getName() + "' had nothing to fix");
}

} catch (Exception e) {
e.printStackTrace();
p.sendMessage(ChatColor.RED + "Failed to load file: " + ChatColor.GREEN + file.getName());
tell(p, "&cFailed to load file: &a" + file.getName());
}

}

p.sendMessage(ChatColor.GREEN + "PlayerData files modified");
tell(p, "&aPlayer data files have been fixed for '" + args[0] + "'");

return true;
}

private Optional<NbtCompound> findByName(NbtList list, String name) {
return list.stream()
.filter(it -> it instanceof NbtCompound)
.map(it -> (NbtCompound) it)
.filter(it -> it.getString("Name", "ERR").equals(name))
.findFirst();
}

private void tell(CommandSender sender, String message) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', String.format("&8\u00bb %s", message)));
}

private void log(String msg) {
plugin.getLogger().info(msg);
}

private class ScrewYouLambdas {
private boolean dirty = false;

public void markDirty() {
dirty = true;
}

public boolean isDirty() {
return dirty;
}

public void reset() {
dirty = false;
}
}

}
2 changes: 1 addition & 1 deletion src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: OCMFixer
main: com.rayzr522.ocmfixer.OCMFixerPlugin
version: 1.0.0
version: 1.1.0
description: Reads playerdata files and fixes the attack speed
commands:
read:
Expand Down

1 comment on commit 8dcd58c

@mibby
Copy link

@mibby mibby commented on 8dcd58c Mar 5, 2017

Choose a reason for hiding this comment

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

@rayzr522 The wiki for default attackDamage attribute seems to be incorrect.
http://minecraft.gamepedia.com/Attribute

Checking the default set in singleplayer minecraft 1.11.2 with an nbt editor, it is set to a base of 1.0 not a base of 2.0.

Edit: I slightly modified the source to check against all attribute defaults. Feel free to add it and/or correct the default damage if you'd like. I would have included flySpeed and walkSpeed as well but since it is categorized in the 'abilities' nbt section, I didn't add it since I didn't want to break something.

                // Reset attack speed
                findByName(list, "generic.attackSpeed").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 4.0) {
                        return;
                    }

                    log("Resetting attack speed to default (4.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 4.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset attack damage
                findByName(list, "generic.attackDamage").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 1.0) {
                        return;
                    }

                    log("Resetting attack damage to default (1.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 1.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset luck
                findByName(list, "generic.luck").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 0.0) {
                        return;
                    }

                    log("Resetting luck to default (0.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 0.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset armor
                findByName(list, "generic.armor").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 0.0) {
                        return;
                    }

                    log("Resetting armor to default (0.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 0.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset armor toughness
                findByName(list, "generic.armorToughness").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 0.0) {
                        return;
                    }

                    log("Resetting armor toughness to default (0.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 0.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset knockback resistance
                findByName(list, "generic.knockbackResistance").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 0.0) {
                        return;
                    }

                    log("Resetting knockback resistance to default (0.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 0.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset max health
                findByName(list, "generic.maxHealth").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 20.0) {
                        return;
                    }

                    log("Resetting max health to default (20.0)");

                    list.remove(attribute);
                    attribute.putPath("Base", 20.0);
                    list.add(attribute);

                    state.markDirty();
                });

                // Reset movement speed
                findByName(list, "generic.movementSpeed").ifPresent(attribute -> {
                    if (attribute.getDouble("Base", 0.0) == 0.100000001490116) {
                        return;
                    }

                    log("Resetting movement speed to default (0.100000001490116)");

                    list.remove(attribute);
                    attribute.putPath("Base", 0.100000001490116);
                    list.add(attribute);

                    state.markDirty();
                });

Please sign in to comment.