Me and a couple of friends are working on a Java Plugin for bukkit/craftbukkit private servers for minecraft. A very large list has been designed of things we wish to implement before we declare it a certain version. We're closing in on a half-playable plugin!

Functions Implemented:
> Player Information created in a SQL database on first login
> Player Information loaded from SQL on subsequent logins
> Player Information saved in SQL on logouts/server shutdowns
> Breaking blocks provides custom exp amounts per-item with level based drops
> Incremental exp curve makes farming materials redundant after lvl 20
> 5 SP earned per level, 1 TP
> Stats STR,DEX,LUK,INT have been listed
> SP can be distributed into stats by command
> Breaking a block stores the time at those coordinates in order to be called later


Essentially, the next thing on the list is finishing the Time-limited exp from blocks to prevent exp farming. A placed block will not provide exp until 24 hours have passed. I'm currently having some problems implementing this using a HashMap...


Code:

// coordinate is a class defining x,y,z
   public class coord {
      Integer X;
      Integer Y;
      Integer Z;
   }
   public class newblock {
      Block block;
      long date;
   }
// newblock is a class defining a minecraft block, and the time it was placed (long, in miliseconds)
private final HashMap<coord, newblock> blockmap = new HashMap<coord, newblock>();
// called by blockListener to modify the HashMap
   public void DestroyBlock(Block block) {
      coord loc = new coord();
      loc.X = (int) block.getLocation().getX();
      loc.Y = (int) block.getLocation().getY();
      loc.Z = (int) block.getLocation().getZ();
      // debug output proved loc is retrieving the proper values

      // blockmap.containsKey(loc) ALWAYS returns false?
      if (blockmap.containsKey(loc)) {
         blockmap.remove(loc);
         System.out.println("MyLeveler: Block removed from map");
      } else {
         System.out.println("MyLeveler: Block removal failed, did not exist");
      }
   }
// this code is called in order to save a new blocks data
   public void SetBlock(Block block) {
      coord loc = new coord();
      long blk = 0;
      loc.X = (int) block.getLocation().getX();
      loc.Y = (int) block.getLocation().getY();
      loc.Z = (int) block.getLocation().getZ();
      // confirmed, x y and z are returning same values as destroyed
      blk.block = block;
      blk.date = System.currentTimeMillis();
      if (blockmap.containsKey(loc)) {
         // again, containsKey returns FALSE;
         blockmap.remove(loc);
         blockmap.put(loc, blk);
         System.out.println("MyLeveler: Block Existed, Updated");
      } else {
         blockmap.put(loc, blk);
         System.out.println("MyLeveler: Block added to " + loc.X + ", " + loc.Y + ", " + loc.Z);
         System.out.println("MyLeveler: total blocks saved: " + blockmap.size());
         // confirmed, size increases, but continues to add same key data
      }
   }
// collect the block data
   public newblock GetBlock(Integer X, Integer Y, Integer Z) {
      coord loc = new coord();
      loc.X = X;
      loc.Y = Y;
      loc.Z = Z;
      newblock blk = new newblock();
      // blockmap.get(loc) returns NULL, no data found
      blk.date = blockmap.get(loc);
      return blk;
   }


This isn't all of the code, but it is the only code containing both the creation, checking, and deletion of the pertinent data. As you can read, I am not able to read any of the data using coord loc; I don't understand why not?? I can't exactly press a pause button and look through a list of the data saved when i cant even get ANY data >.>

Keep in note that it's not actually causing any errors, rather just not working as desired.

*double post merge by comic*

Am i not allowed to store/call a Key in the form of a custom coord? is my code for coord properly formatted for its purpose?
This is not really a programming-related response, but on Evocatus, the server I moderate that most of Cemetech's Minecraft players use, we use the Heroes mod for RPG features. I strongly suggest you check it out, either as a possible reason not to make you plugin, or as motivation to come up with your own unique features that they don't have. It seems quite powerful to me, although of course it has a few bugs and problems that are gradually getting smoothed out, like all Minecraft plugins.
This is a stand-alone plugin... Meaning we're building all the functionality we want in it, without using outside plugins that may actually complicate our process...

Our list of functions we plan to add is getting rather long actually >.> and as such, there's a lot of room for another plugin to mess up our work. We need this function for 2 reasons though. 1) we need to prevent exp farming, 2) we need to track a small list of changes done to each block to allow a toggle for active structure protection... anti griefing as it were. We're designing a very unique collection of plugins, and thus cant exactly strip from other open-source plugins, since they don't typically exist in the manner we're using?

[update]

This error has been fixed, you cannot use a custom class the way i was defining it as a Key to a hashmap, but you can use one as a value. I simply imported and used bukkits Location class/type from import org.bukkit.Location;

On to the next complication!

Now that the data is stored to prevent players from farming blocks for exp, we now need to keep the variables over server reboots or shutdowns.

Trying to save a hashmap when the plugin closes, and reload it when the plugin loads. This will allow me to keep these variables past server shutdown. For said operation, i found in a tutorial:
http://wiki.bukkit.org/HUGE_Plugin_Tutorial#Saving.2FLoading_a_HashMap

A few quick notes, none of the code in that tutorial explains how to select the root plugin folder as the start... second, it doesn't seem to support file path creation if it doesnt exist... bypassed this temporarily by creating it manually, and linking to it with an entire address. This works for me, but will make it unaccessible for others. And lastly, when all is said and done, I'm still having errors with serialization?



Code:

public class MyLeveler extends JavaPlugin implements Serializable {
   /**
    *
    */
   private static final long serialVersionUID = 1L;
   // ...
   HashMap<Location, newblock> blockmap = new HashMap<Location, newblock>();
   public void onEnable() {
   // ...
      try{
      // failed attempt to create directory from root. Function did not exist as an import?
          //Path p1 = FileSystems.getDefault().getPath("MyLeveler\\blockmap.hash");
          ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Kitsune\\Desktop\\craftbukkit\\server\\plugins\\MyLeveler\\blockmap.hash"));
          Object result = ois.readObject();
          //you can feel free to cast result to HashMap<Player,Boolean> if you know there's that HashMap in the file
          blockmap = (HashMap<Location, newblock>) result;
          ois.close();
          //return (HashMap<Player,Boolean>)result;
          System.out.println("MyLeveler: Block data loaded");
       }catch(Exception e){
          //java.io.FileNotFoundException - file doesnt exist
          //java.io.EOFException - file is empty
          System.out.println("MyLeveler: Error loading block data");
          e.printStackTrace();
       }
   // ...
}


"implements Serializable" was supposed to be a fix for this error, but it did nothing... the next line was supposed to be a warning fix, which still did nothing but remove the warning?


Code:

02:09:05 [INFO] MyLeveler: Error saving block data
02:09:05 [SEVERE] java.io.NotSerializableException: org.bukkit.Location
02:09:05 [SEVERE]       at java.io.ObjectOutputStream.writeObject0(Unknown Source)
02:09:05 [SEVERE]       at java.io.ObjectOutputStream.writeObject(Unknown Source)
02:09:05 [SEVERE]       at java.util.HashMap.writeObject(Unknown Source)
02:09:05 [SEVERE]       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
02:09:05 [SEVERE]       at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
02:09:05 [SEVERE]       at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
02:09:05 [SEVERE]       at java.lang.reflect.Method.invoke(Unknown Source)
02:09:05 [SEVERE]       at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
02:09:05 [SEVERE]       at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
02:09:05 [SEVERE]       at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
02:09:05 [SEVERE]       at java.io.ObjectOutputStream.writeObject0(Unknown Source)
02:09:05 [SEVERE]       at java.io.ObjectOutputStream.writeObject(Unknown Source)
02:09:05 [SEVERE]       at komak57.MyLeveler.MyLeveler.onDisable(MyLeveler.java:310)
02:09:05 [SEVERE]       at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:128)
02:09:05 [SEVERE]       at org.bukkit.plugin.java.JavaPluginLoader.disablePlugin(JavaPluginLoader.java:899)
02:09:05 [SEVERE]       at org.bukkit.plugin.SimplePluginManager.disablePlugin(SimplePluginManager.java:288)
02:09:05 [SEVERE]       at org.bukkit.plugin.SimplePluginManager.disablePlugins(SimplePluginManager.java:281)
02:09:05 [SEVERE]       at org.bukkit.craftbukkit.CraftServer.disablePlugins(CraftServer.java:157)
02:09:05 [SEVERE]       at net.minecraft.server.MinecraftServer.stop(MinecraftServer.java:312)
02:09:05 [SEVERE]       at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:391)
02:09:05 [SEVERE]       at net.minecraft.server.ThreadServerApplication.run(SourceFile:422)
02:09:05 [INFO] MyLeveler Disabled...;
02:09:05 [INFO] Saving chunks
02:09:06 [INFO] Stopping server
02:09:06 [INFO] Saving chunks
>Server shut down successfully!
Press any key to continue . . .


And before you say "eeew, flat file storage" this is actually quite convenient since its only storing and recalling them when time is least destructive to the servers players.
Do hashmaps not inherently implement Serializable? I'm very disappointed, if not, as you seem to be implying. Serialization is Java's equivalent to Python's pickling, and is a fairly powerful tool. If you say it implements Serializable, you have to actually implement the interface somewhere.
Not sure what you mean by actually implement the interface? I'm basically running off of this guide to serialisation, which is rather rough to translate to my particular needs:

http://java.sun.com/developer/technicalArticles/Programming/serialization/
Well, if Serializable does everything you want it to do, you can just implement it as you are. If it misses something for some reason, you need to use the Externalizable interface, which is what I was referring to. On third glance, your code above looks decent to me. I notice that the exception is from onDisable, whereas you showed the code of onEnable? Did you add the interface implementation in both places?
Very similar code was used in both sections, yes. But the error in the onDisable fails, the onEnable will surely fail the next time.

I removed the SerialVersionUID, added a suppression, loaded the plugin, got the onEnable error, then stopped the server, allowing the onDisable to parse. Flat file was stored successfully. Loaded the server again, it was loaded successfully! (in this case, the hashmap was empty)

Next i did a full test. when it loaded without error, i logged in, destroyed a few blocks, checked exp, placed a few blocks, destroyed em again, checked exp. This was successful as always at preventing exp gain. Then i stopped the server... threw the same exception. It seems that once i have data in the hashmap, it wont save? I'm wondering if this falls back to my earlier issue with using class coord {} rather than a standard class/type, but in this case, its because of newblock ?

[update]
having the serial key or not doesn't seem to effect the save/load process until the line "ois.defaultReadObject();" or "oos.defaultWriteObjecT();" is added after the file has been opened. This makes sure the file was saved using this particular java program i presume. However, adding the line still causes an error i wasn't watching for (intentionally)... tried to use "throws ClassNotFoundException, IOException", and it somehow threw "NotActiveException - if the stream is not currently reading objects."

here's another source i came upon looking for a solution that helped with this particular part, but im still not getting results?
http://www.javapractices.com/topic/TopicAction.do?Id=45
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement