[LIB/TUT] Improved Offline Player - NBT Editing made easy!

Discussion in 'Resources' started by one4me, Aug 11, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    one4me

    Improved Offline Player -v1.6.0
    Note: Current version will be going under some major changes.
    Info
    Originally I just wanted a way to edit an offline player's inventory, and this is what I came up with. Since the only two ways I found to edit an offline player: loading the player as an entity and then editing the player, or by messing around with the NBT format - and because I fail a lot more than normal when it comes to entities I ended up with spending a little bit of time learning how NBT works. To save everyone else the time it takes to learn how NBT is set up I went all out and made a nice class for everyone.

    Summary
    This class will give you nice methods for editing almost every detail of an offline player, including but not limited to, their inventory and location.

    Download - TEMP.java (Includes License)
    To make use of all the methods this class provides just copy it into whatever package you want and import it into whichever class you want.

    Usage
    Add these lines to wherever you want to - replacing PlayerName with the player's name.
    Code:
    ImprovedOfflinePlayer iop = new ImprovedOfflinePlayer("PlayerName");
    if(!iop.exists()) return;
    The second line isn't required, but I suggest leaving it as you you'll get a lot of NPEs otherwise.
    And now that you have an ImprovedOfflinePlayer you should have access to all of it's methods.

    Also, in order to make sure data is always saved, the file is saved whenever a variable is changed. This does make it easy to code, but it can be inefficient. If you would like to turn off the autosave feature and save manually you would do this:
    Code:
    /*This stops the file from saving everytime a variable is set*/
    iop.setAutoSave(false);
    
    /*This forces the file to be saved with all of the updated variables*/
    iop.savePlayerData();
    
    Example - VanillaExtract
    This is just a simple plugin I'm working on which allows for the majoriy of vanilla Minecraft commands to work on players even if they're not online.

    Problems
    This is still a work in progress, so if you find any bugs please let me know. Although I don't no why you would use one of these methods for a player that is currently online, in the event that you do, know that nothing will happen unless you do player.loadData()

    Future
    I plan to add a few more features, and unless Bukkit adds their own API that makes this obsolete I'll try to keep this updated. There will most likely be some bugs I missed and although I'll try to fix them myself, help is always appreciated. Also, if you can improve one of my methods and don't mind sharing how post it below and I may add it to the class.

    Other
    If anyone has any questions feel free to post them.

    Edit - I forgot to mention, this is out under the MIT license.
     
  2. Offline

    p000ison

    Nice nice :p
     
  3. Offline

    sl1nk3

    Awesome, thanks !
     
  4. Offline

    one4me

    I updated this for 1.4.2. It does reference Craftbukkit a bit more than I'd like, but for the time being there's not much I can do about that. I put in a temporary fix for getting and setting the inventory, so those methods should now work properly.
     
  5. Offline

    CorrieKay

    Assuming we're talking about the code that md_5 wrote, why is it temporary? thats how the server itsself loads the item stack from the dat file. That should be final.
     
  6. Offline

    one4me

    I am referring to that code, I was attempting to remove the need to reference Craftbukkit. However, that isn't possible at the moment (as far as I know) which is why I have the temporary fix in place.

    It's not likely that I'll be changing it any time soon though, just if it's ever possible to do by using the Bukkit API alone I'll be doing it that way.
     
  7. Offline

    CorrieKay

    Even if the bukkit api eventually puts in this kind of support, (which, as md_5 said, is unlikely) This is still the same code thats going to be run. However, i do see merit in that they will automatically handle the obfuscated methods.

    In the end though, if they DID, your ImprovedOfflinePlayer object will be obsolete.
     
  8. Offline

    one4me

    In the old method, as you saw, I went through the tags myself and created the ItemStack. This wasn't very effective because other than the methods that allowed me to add enchantments tags to an ItemStack, adding tags to an ItemStack was not possible through the Bukkit API.

    That meant that data such as the display tags, book tags, and custom potion effects were not able to added to an ItemStack without referencing Craftbukkit. This wouldn't be a problem, but like you said, those methods change every once in a while. Also not everyone compiles against Craftbukkit

    Now, it has already been said that methods to directly edit NBTTags will not be added to the API, so this class won't become obsolete any time soon. But if methods to add display tags, book tags, and custom potion effect tags to ItemStacks were added (similar to how enchantment tags are added) to the Bukkit API, then what I was trying to do would entirely be possible.

    Still, I doubt any of those methods are going to be added to the Bukkit API any time soon. Someone did say he was working on adding book support to the API a few months ago, but until that happens the methods md_5 wrote are likely to stay - which by the looks of it will be quite some time.
     
  9. Offline

    md_5

    if you want to learn more about how NBT works, including your own NBT library would be a good exercise. And it would help you create a standalone application to edit player data.
     
  10. Offline

    BrainStone

    I missed two functions so I added them. Here they are:

    Code:java
    1. public int getDeathTime() {
    2. return compound.getShort("Death Time");
    3. }
    4.  
    5. public void setDeathTime(int input) {
    6. compound.setShort("Death Time", (short) input);
    7. if (autosave) {
    8. this.savePlayerData();
    9. }
    10. }


    This libary helped me a lot! Thanks!
     
  11. Offline

    TeamJesus

    alright updating this to 1.6.2 and the problem i am having is with the enderchest saving... the code is:

    Code:java
    1. public Inventory getEnderChest() {
    2. InventoryEnderChest endchest = new InventoryEnderChest();
    3. endchest.a(this.compound.getList("EnderItems"));
    4. return new CraftInventory(endchest);
    5. }
    6. public void setEnderChest(Inventory inventory) {
    7. this.compound.set("EnderItems", ((InventoryEnderChest)((CraftInventory)inventory).getInventory()).g());
    8. if(this.autosave) savePlayerData();
    9. }


    the line thats casing problems is... this.compount.set....
    the error on line : The Metod set(String, NBTBas) in the type NBTTagCompound is not applicable for the arguments (String, void)

    Any ideas?

    Cheers
     
  12. Offline

    one4me

    TeamJesus
    In 1.6.4 you would use h() not g().

    Anyways I've been somewhat busy over the last year, but I'll try to get this updated within a week or two (hopefully removing NMS from the code.)

    In the meantime, here's a temporary version for 1.6.4: TEMP.java. Now I can't say I've tested this, so you may run into some stuff that still may not work. However, in the event that you do run into any more problems just let me know.
     
  13. Offline

    TeamJesus

    sweet i will have a look at it, i was updating your original ( just to get it working lol ) but yea thanks :)

    hrm yea now to figure out how to use the inventory bit... any example to use the inventory on it??

    Thanks

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 27, 2016
  14. Offline

    one4me

    Here's an example for the player's inventory:
    Code:
    /*Create an instance of ImprovedOfflinePlayer*/
    ImprovedOfflinePlayer iop = new ImprovedOfflinePlayer("PlayerName");
    
    /*Make sure the player data exists before using it*/
    if(iop.exists()) {
    
      /*Get the player's inventory and armor*/
      PlayerInventory inv = iop.getInventory();
    
      /*Add an item to the Inventory (You should be able to use any method available to org.bukkit.inventory.PlayerInventory)*/
      inv.addItem(new ItemStack(Material.COCOA));
    
      /*Set the player's inventory to the new inventory*/
      iop.setInventory(inv);
    }
    

    And here's an example for the player's ender chest:
    Code:
    /*Create an instance of ImprovedOfflinePlayer*/
    ImprovedOfflinePlayer iop = new ImprovedOfflinePlayer("PlayerName");
    
    /*Make sure the player data exists before using it*/
    if(iop.exists()) {
    
      /*Get the ender chest*/
      Inventory inv = iop.getEnderChest();
    
      /*Add an item to the ender chest (You should be able to use any method available to org.bukkit.inventory.Inventory)*/
      inv.addItem(new ItemStack(Material.COCOA));
    
      /*Set the ender chest to the new inventory*/
      iop.setEnderChest(inv);
    }
    
     
    TeamJesus likes this.
  15. Offline

    TeamJesus

    great... um sorry again for dumb questions :D so how do i pop up a window with said inventory instead of adding stuff to it? ( yea i am a bit of a n00b but i'm getting there )

    thanks for putting up wid me :)
     
  16. Offline

    Comphenix

    Try player.openInventory(inventory).

    Very compact and readable, though it's unfortunate that it depends on CraftBukkit and a versioned package.

    If you're working on eliminating NMS anyway, perhaps you could save some time by using my NBT library? The latest version does support loading and saving NBT files. Of course, you will have to deal with loading/saving inventories and item stacks yourself, but that can be accomplished with reflection (use getHandle() to get the NBTTagCompound of a wrapped compound) or simply by duplicating code.
     
  17. Offline

    one4me

    The problem is that I wanted for this to not be dependent on NMS or CB. But like a year ago, as far as I know the only tag that can be edited with Bukkit alone is enchantments (which is a bit annoying.) Once CB was a dependency I didn't see a reason not to use NMS (other than it requiring a little bit more maintenance.)

    Still, thanks for making the library, it'll easily save me time from making my own alternative to the NMS NBT classes; I'll have to mess around with it this weekend.
     
    TeamJesus likes this.
  18. Offline

    clienthax

    Get this when attempting to save a ender chest from a created inventory on 1.5.2

    Caused by: java.lang.ClassCastException: org.bukkit.craftbukkit.v1_5_R3.inventory.CraftInventoryCustom$MinecraftInventory cannot be cast to net.minecraft.inventory.InventoryEnderChest
    at com.clienthax.oldEnderChest.ImprovedOfflinePlayer.setEnderChest(ImprovedOfflinePlayer.java:203)
    at com.clienthax.oldEnderChest.OldEnderChest.onInvClose(OldEnderChest.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:361)
    ... 16 more
     
  19. Offline

    one4me

    It would help to see your CraftInventoryCustom class (MinecraftInventory class as well) to tell you exactly what the problem is, but from the stack trace it appears that your custom inventory is not a valid net.minecraft.server.InventoryEnderChest.
     
  20. Offline

    clienthax

    thats just the instance returned by Bukkit.createInventory

    https://github.com/Bukkit/CraftBukk...aftbukkit/inventory/CraftInventoryCustom.java
     
  21. Offline

    one4me

    That's my fault, setEnderChest(Inventory inventory) assumes the parameter is a net.minecraft.server.InventoryEnderChest, even though that may not always be the case. I'll fix that when I update it. In the mean time try adding something like this to your class which will convert your inventory to a net.minecraft.server.InventoryEnderChest:
    Code:
    public Inventory asEnderChest() {
      Inventory enderchest = new CraftInventory(new InventoryEnderChest());
      enderchest.setContents(getContents());
      return enderchest;
    }
    
    Edit: Oops, fixed a little error in that method which would definitely cause an error as it didn't even return anything. Anyways, that's fixed now.
     
  22. Offline

    clienthax

    Perfect!, thanks

    public Inventory asEnderChest(Inventory inv) {
    Inventory enderchest = new CraftInventory(new InventoryEnderChest());
    enderchest.setContents(inv.getContents());
    return enderchest;
    }

    public void setEnderChest(Inventory inventory) {
    if(!(inventory instanceof InventoryEnderChest))
    inventory = asEnderChest(inventory);

    CraftInventory cinv = (CraftInventory)inventory;
    InventoryEnderChest enderchest = (InventoryEnderChest)cinv.getInventory();
    compound.set("EnderItems", enderchest.h());//.saveInventoryToNBT());

    if(this.autosave) savePlayerData();
    }

    Code:java
    1. public Inventory asEnderChest(Inventory inv) {
    2. Inventory enderchest = new CraftInventory(new InventoryEnderChest());
    3. enderchest.setContents(inv.getContents());
    4. return enderchest;
    5. }
    6.  
    7. public void setEnderChest(Inventory inventory) {
    8. if(!(inventory instanceof InventoryEnderChest))
    9. inventory = asEnderChest(inventory);
    10.  
    11. CraftInventory cinv = (CraftInventory)inventory;
    12. InventoryEnderChest enderchest = (InventoryEnderChest)cinv.getInventory();
    13. compound.set("EnderItems", enderchest.h());//.saveInventoryToNBT());
    14.  
    15. if(this.autosave) savePlayerData();
    16. }


    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 27, 2016
  23. Offline

    ThatOneDev123

    Can you please update the link?
     
Thread Status:
Not open for further replies.

Share This Page