OOP Bukkit

Discussion in 'Plugin Development' started by jthort, May 24, 2014.

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

    jthort

    Hey Bukkit, this is more of a general question,

    Say I'm creating a town plugin where players have the ability to build up their own town. There is a class called "Town" so that when I want a new town created, I can do
    Code:
    Town town = new Town();
    The town attributes have to be saved to a file for it to handle reloads, so that leads to my overall question..

    Would it be more efficient to create instances of that class and save and load the objects on the onEnable and onDisable, or rather have a static class and have methods such as this
    Code:
    public static String getTownLeader(String townName){
              return mainClass.getConfig().getString(this.townName+".Leader");
          }
    Although a static class seems simpler, having an instance class has it's advantages such as efficiency. I would imagine that getting a value from the config every time would be resource consuming, rather than load an object with all of those attributes pre-defined. But on the down side it would be resource consuming when it comes to reloads and restarts.

    If you have a answer, please provide facts rather than saying "I think this". Back up your statements rather than making assumptions. Thanks
     
  2. Offline

    AronTheGamer

    You can do it like

    Code:java
    1. import org.bukkit.Location;
    2.  
    3. import java.util.LinkedList;
    4.  
    5. public class Town {
    6. private String TownName;
    7. private String TownLeader;
    8. private Location TownHome;
    9. private LinkedList<String> TownPlayers = new LinkedList<String>();
    10.  
    11. public Town(String Name, String Leader, Location Home ) {
    12. TownName = Name;
    13. TownLeader = Leader;
    14. TownHome = Home;
    15. }
    16.  
    17. public String getName() {
    18. return TownName;
    19. }
    20.  
    21. public String getLeader() {
    22. return TownLeader;
    23. }
    24.  
    25. public Location getHome() {
    26. return Home;
    27. }
    28.  
    29. public void setName( String Name ) {
    30. TownName = Name;
    31. }
    32.  
    33. public void setLeader( String Leader ) {
    34. TownLeader = Leader;
    35. }
    36.  
    37. public void setHome( Location Home ) {
    38. TownHome = Home;
    39. }
    40.  
    41. public void addPlayer( String p) {
    42. if( !TownPlayers.contains( p ) )
    43. TownPlayers.add( p );
    44. }
    45.  
    46. public void removePlayer( String p ) {
    47. if( TownPlayers.contains( p ) )
    48. TownPlayers.remove( p );
    49. }
    50.  
    51. public boolean hasPlayer( String p ) {
    52. return TownPlayers.contains( p );
    53. }
    54.  
    55. public LinkedList<String> getPlayers() {
    56. return TownPlayers;
    57. }
    58.  
    59. }


    TownManager
    Code:java
    1.  
    2.  
    3. class TownManager {
    4.  
    5. private HashMap<String, Town> Towns = new HashMap<String, Town>();
    6.  
    7. public Town getTown( String Name ) {
    8. if( Towns.containsKey ( Name )
    9. return Towns.get( Name );
    10. return null;
    11. }
    12.  
    13. public boolean hasTown( String Name ) {
    14. return Towns.containsKey( Name );
    15. }
    16.  
    17. public void addTown( String Name, String Leader, Location Home ) {
    18. if( !Towns.containsKey( Name ) )
    19. Towns.put( Name, new Town( Name, Leader, Home ) );
    20. }
    21.  
    22. public Town getTownByPlayer( String p ) {
    23. for( Town t : Towns.values() ) {
    24. if( t.hasPlayer( p ) ) return t;
    25. }
    26. return null;
    27. }
    28.  
    29. public HashMap<String, Town> getTowns() {
    30. return Towns;
    31. }
    32.  
    33. }
    34.  
    35.  


    Put this in the top of your JavaPlugin class
    Code:java
    1.  
    2. private TownManager = new TownManager();
    3.  


    And to load towns from the config:
    Code:java
    1.  
    2. ConfigurationSection Towns = getConfig().getConfigurationSection("towns");
    3.  
    4. for( String key : Towns.getKeys( false ) ) {
    5. ConfigurationSection TownSection = Towns.getConfigurationSection( key );
    6.  
    7. String Name = TownSection.getString( "name" );
    8. String Leader = TownSection.getString( "leader" );
    9. double homeX = TownSection.getDouble( "home.x" );
    10. double homeY = TownSection.getDouble( "home.y" );
    11. double homeZ = TownSection.getDouble( "home.z" );
    12. double homeW = TownSection.getDouble( "home.w" );
    13. World homeWorld = getServer().getWorld( homeW );
    14. Location Home = new Location( homeWorld, homeX, homeY, homeZ );
    15.  
    16. TownManager.addTown( Name, Leader, Home );
    17.  
    18. for( String p : TownSection.getStringList("players") ) {
    19. TownManager.getTown( Name ).addPlayer( p );
    20. }
    21. }
    22.  


    And the saving:
    Code:java
    1.  
    2. getConfig().set( "towns", null );
    3.  
    4. for( Town t : TownManager.getTowns().values() ) {
    5. String SectionPath = "towns." + t.getName();
    6. getConfig().set( SectionPath + ".name", t.getName() );
    7. getConfig().set( SectionPath + ".leader", t.geLeader() );
    8. getConfig().set( SectionPath + ".home.x", t.getHome().getX() );
    9. getConfig().set( SectionPath + ".home.y", t.getHome().getY() );
    10. getConfig().set( SectionPath + ".home.z", t.getHome().getZ() );
    11. getConfig().set( SectionPath + ".home.w", t.getHome().getWorld().getName() );
    12. }
    13. String[] players = t.getPlayers.toArray( String[0] );
    14. getConfig(SectionPath + ".players");
    15.  


    Wrote on iPhone, not sure if it works but should.

    Took me around an hour to write on my iPhone #notepad
     
  3. Offline

    joeygallegos

    Was going to say the same exact thing AronTheGamer was.. And boy, I'm amazed he wrote all that on an iPhone
     
    AronTheGamer likes this.
  4. Offline

    CoderMusgrove

    Loading and storing the resources will always be better, than always grabbing from the configuration each time. It keeps efficiency while it's running, but reloads will be a tiny bit slower. Overall, take the assumption that there are 100 players on the server. If they each keep using commands that call the getConfig() method, and they do this once every 10 seconds, the resources used to grab from the config, because of the file streams of loading, reading, and closing will always take a bit of time. Loading it on startup will only make you have to do this once, so overall loading the information on startup is better, and I recommend doing as AronTheGamer said.
     
  5. Offline

    Mrawesomecookie

    AronTheGamer
    Wouldn't you want to singleton your TownManager if your accessing it in a non-static way?
     
    xTigerRebornx likes this.
  6. Offline

    xTigerRebornx

    CoderMusgrove If you are referring to FileConfiguration/YamlConfiguration when you say getConfig(), from what I've researched, it doesn't actually do any reading from a file other then loading when its made), it uses a MemorySection (which is generally a Map<String, Object>), so unless you made a new YamlConfiguration everytime you called get(), you wouldn't have the lag that comes from the loading/reading/closing of a file stream.
    It'd still be easier to just store the resources, rather then constantly deserialize/serialize the data from a FileConfiguration everytime you want something, but it'd just be personal preference at that point.
     
    CoderMusgrove likes this.
  7. Offline

    jthort

    xTigerRebornx Mrawesomecookie CoderMusgrove joeygallegos AronTheGamer Thanks for all the comments, I'll show you how I approached it

    Loading and saving
    Code:java
    1. public void setUpDefaultConfig(){
    2. FileConfiguration config = mainClass.getConfig();
    3. try{
    4. for(String key : config.getKeys(false)){
    5. String townName = config.getString(key+".Name");
    6. String townLeader = config.getString(key+".Leader");
    7. double x = config.getDouble(key+".location.x");
    8. double y = config.getDouble(key+".location.y");
    9. double z = config.getDouble(key+".location.z");
    10. World world = Bukkit.getWorld(config.getString(key+".location.World"));
    11. Location townCenter = new Location(world,x,y,z);
    12. boolean open = config.getBoolean(key+".Open");
    13. int tier = config.getInt(key+".Tier");
    14. Town town = new Town(townCenter, townName, townLeader, tier, mainClass);
    15. towns.add(town);
    16. town.setOpen(open);
    17. for(String member: config.getStringList(key+".Members")){
    18. town.addMember(member);
    19. }
    20. for(String member: config.getStringList(key+".Admins")){
    21. town.addAdmin(member);
    22. }
    23.  
    24. }
    25.  
    26.  
    27.  
    28.  
    29. }catch(Exception ex){
    30. ex.printStackTrace();
    31. }
    32. config.options().copyDefaults(true);
    33. mainClass.saveConfig();
    34.  
    35. }

    Instance Class
    Code:java
    1. public class Town {
    2.  
    3. public Main mainClass;
    4. String townName;
    5. List<String> members = new ArrayList<String>();
    6. List<String> admins = new ArrayList<String>();
    7. UpgradeChain townTier;
    8. TownAccount townAccount;
    9. Plans plans;
    10.  
    11. public Town(Location townLocation, String townName, String townLeader, int tier, Main mainClass){
    12. //Set up variables
    13. this.mainClass = mainClass;
    14. this.townName = townName;
    15. //Create starting Block
    16. Block startingBlock = townLocation.getWorld().getBlockAt(townLocation);
    17. if(!(startingBlock.getType().equals(Material.GOLD_BLOCK))){
    18. startingBlock.setType(Material.GOLD_BLOCK);
    19. }
    20.  
    21.  
    22. mainClass.getConfig().addDefault(townName+".Name",townName);
    23. mainClass.getConfig().addDefault(townName+".location."+"x",startingBlock.getLocation().getX());
    24. mainClass.getConfig().addDefault(townName+".location."+"y",startingBlock.getLocation().getY());
    25. mainClass.getConfig().addDefault(townName+".location."+"z",startingBlock.getLocation().getZ());
    26. mainClass.getConfig().addDefault(townName+".location."+"World",startingBlock.getLocation().getWorld().getName().toString());
    27. mainClass.getConfig().addDefault(townName+".Leader",townLeader);
    28. mainClass.getConfig().addDefault(townName+".Open", false);
    29. mainClass.getConfig().addDefault(townName+".Members", members);
    30. mainClass.getConfig().addDefault(townName+".Admins", admins);
    31. mainClass.getConfig().options().copyDefaults(true);
    32.  
    33. townTier = new UpgradeChain(townName, tier, mainClass);
    34. townAccount= new TownAccount(townName, mainClass);
    35. plans = new Plans(mainClass, townName);
    36.  
    37. }
    38. public Location getTownCenter(){
    39. return (new Location(
    40. Bukkit.getWorld(mainClass.getConfig().getString(this.townName+".location."+"World")),
    41. mainClass.getConfig().getDouble(this.townName+".location."+"x"),
    42. mainClass.getConfig().getDouble(this.townName+".location."+"y"),
    43. mainClass.getConfig().getDouble(this.townName+".location."+"z")
    44. ));
    45. }
    46. public UpgradeChain getTownTier(){
    47. return townTier;
    48. }
    49. public TownAccount getAccount(){
    50. return townAccount;
    51. }
    52. public Plans getPlans(){
    53. return plans;
    54. }
    55. public String getTownLeader(){
    56. return mainClass.getConfig().getString(this.townName+".Leader");
    57. }
    58. public String getTownName(){
    59. return mainClass.getConfig().getString(this.townName+".Name");
    60. }
    61. public void setTownName(String townName){
    62. mainClass.getConfig().set(this.townName+".Name", townName);
    63. mainClass.saveConfig();
    64. }
    65. public void setTownLeader(String newLeader){
    66. mainClass.getConfig().set(this.townName+".Leader", newLeader);
    67. mainClass.saveConfig();
    68. }
    69. public void setOpen(boolean tf){
    70. if(tf){
    71. mainClass.getConfig().set(this.townName+".Open", true);
    72. }else{
    73. mainClass.getConfig().set(this.townName+".Open", false);
    74. }
    75. mainClass.saveConfig();
    76. }
    77. public boolean isOpen(){
    78. return mainClass.getConfig().getBoolean(this.townName+".Open");
    79. }
    80. public void addMember(String player){
    81. members.add(player);
    82. mainClass.getConfig().set(this.townName+".Members", members);
    83. mainClass.saveConfig();
    84. }
    85. public void addAdmin(String player){
    86. admins.add(player);
    87. mainClass.getConfig().set(this.townName+".Admins", admins);
    88. mainClass.saveConfig();
    89. }
    90. public List<String> getMembers(){
    91. return members;
    92. }
    93. public List<String> getAdmins(){
    94. return admins;
    95. }
    96. public void remove(String player){
    97. admins.remove(player);
    98. members.remove(player);
    99. mainClass.getConfig().set(this.townName+".Members", members);
    100. mainClass.saveConfig();
    101. }
    102. public boolean isTownLeader(String player){
    103. if(getTownLeader().equals(player)){
    104. return true;
    105. }
    106. return false;
    107. }
    108. public void sendToAdmins(String message){
    109. for(String player: mainClass.getConfig().getStringList(this.townName+".Admins")){
    110. try{
    111. Player players = Bukkit.getPlayer(player);
    112. players.sendMessage(message);
    113. }catch(Exception ex){
    114.  
    115. }
    116. }
    117. try{
    118. Player player = Bukkit.getPlayer(getTownLeader());
    119. player.sendMessage(message);
    120. }catch(Exception ex){
    121.  
    122. }
    123. }
    124. public void sendToAll(String message){
    125. for(String player: mainClass.getConfig().getStringList(this.townName+".Admins")){
    126. try{
    127. Player players = Bukkit.getPlayer(player);
    128. players.sendMessage(message);
    129. }catch(Exception ex){
    130.  
    131. }
    132. }
    133. try{
    134. Player player = Bukkit.getPlayer(getTownLeader());
    135. player.sendMessage(message);
    136. }catch(Exception ex){
    137.  
    138. }
    139. for(String player: mainClass.getConfig().getStringList(this.townName+".Members")){
    140. try{
    141. Player players = Bukkit.getPlayer(player);
    142. players.sendMessage(message);
    143. }catch(Exception ex){
    144.  
    145. }
    146. }
    147. }
    148.  
    149. public void del(){
    150. File configFile = new File(mainClass.getDataFolder(), "config.yml");
    151. FileConfiguration config = YamlConfiguration.loadConfiguration(configFile);
    152. config.set(this.townName, null);
    153. this.getTownCenter().getBlock().setType(Material.AIR);
    154. TownCommands.towns.remove(this);
    155. try{
    156. config.save(configFile);
    157. mainClass.reloadConfig();
    158. }catch(IOException e){
    159. Logger.getLogger("Minecraft").info("Error while reading config.yml");
    160. }
    161. }
    162.  
    163. public boolean containsLocation(Location location){
    164. int townSize = this.getTownTier().getTierLevel()*20;
    165. double maxX = this.getTownCenter().getX()+(townSize);
    166. double maxZ = this.getTownCenter().getZ()+(townSize);
    167. double minX = this.getTownCenter().getX()-(townSize);
    168. double minZ = this.getTownCenter().getZ()-(townSize);
    169.  
    170. if(location.getX() <= maxX && location.getX() >= minX){
    171. if(location.getZ() <= maxZ && location.getZ() >= minZ){
    172. return true;
    173. }
    174. }
    175. return false;
    176. }
    177.  
    178.  
    179. }
    180.  


    I guess it's exactly what he said
    But as you can see, I did some sort of a mixture. I load up the objects on startup, but I save everything to the config rather than the values. I'm thinking about rewriting it, it's a little messy :)

    Maybe someone could find a way to write some sort of mixture for better efficiency. If there are limited people on, I don't see a problem in just interacting with the config directly. But when it gets to the hundreds it's better to go with the objects load/save
     
  8. Offline

    CoderMusgrove

    Thank you for that helpful hint, it will be taken into consideration next time. I was most likely just thinking of the save in a FileConfiguration, how saving it at the server shutdown is more efficient than actually saving it each time that you do something. I may look into this more.
     
  9. Offline

    jthort

    xTigerRebornx Now that I think about it, wouldn't it be possible to create some sort of library that allows you to save objects to the config.yml, and load them. It would take the object and all it's attributes and split it up to save it, then grab it again and load it back into that object.

    I could find that very helpful. I'm thinking about making it..

    Edit It looks like someone beat me to it https://forums.bukkit.org/threads/l...y-with-load-and-safe-for-your-project.210101/
     
  10. Offline

    Garris0n

    AronTheGamer Now...I know you took the time to write all of that out. And on a mobile phone at that, so I really hate to say this...but please never give people code examples with badly named variables/methods/anything. Use the proper naming conventions to avoid teaching some new coder who happened upon this thread some terrible habits.

    Again, I know you wrote it on a phone...but still. It can cause problems.
     
    ZodiacTheories likes this.
  11. Offline

    jthort

    Garris0n It doesn't matter if the variables are named something or not with what he was giving me. It wasn't the actual code I was looking at, but the idea on how he accomplished this task. He was simply explaining the basic concept behind his code rather than the specifics

    Again as the original question stated, i'm trying to decide the most efficient way to get information from the config, not code of a town class
     
  12. Offline

    Garris0n

    Yes, but some new coder might look at it and say "oh, capital letters at the start of variables are ok". That is what I meant. And you underestimate the habit of some people to copy code without reading it.

    As for the question, it depends. If you get it from the config, it's still stored in memory, but it's in a map. I would personally suggest the "Town" class.
     
  13. Offline

    jthort

    Garris0n Well no matter what it's going to have a town class, the question is if it should be a singleton class or an instance class, or in other words, get it directly from the config or from an instance class.

    But as we stated above I guess it's more of a preference
     
  14. Offline

    Garris0n

    Instance class.
     
  15. Offline

    mythbusterma



    Don't bother, Bukkit has this feature built in with the ConfigurationSerializable interface:
    http://wiki.bukkit.org/Configuration_API_Reference#Serializing_and_Deserializing_Objects

    Simply implement that and add the methods required by the specification, i.e. public Map<String,Object> Serialize() and one of any: constructor that takes a Map<String,Object> as an argument, public deserialize(Map<String,Object>) etc.

    I use these in a lot of my plugins, as long as the object you're adding to the map is a primitive, or implements ConfigurationSerializable itself, this works well.

    EDIT: Strings work too.
     
    jthort likes this.
  16. Offline

    jthort

  17. Offline

    xTrollxDudex

    AronTheGamer
    An hour to write that!??

    It takes me like an hour to write 20 lines of NMS code on a phone... Check and double check obfuscated methods, forget names and parameters, check what it is used for (hardest part), wait for net/minecraft/server to load (lots of time), research past implementation, watch framework design, check field access, check reflection, check conventions, check semicolons, check and double check every thing else... Yes, NMS is hard without an IDE :p
     
  18. Offline

    xize

    I just want to say though while the usage of singleton comes out in handy note don't make your class 90% static and then I mean places where things shouldn't be singleton, instead what you could do is this:

    Code:
    private static SomeConfig conf; //when bussy in a constructor you could make this final though
    
    public static SomeConfig getConfig() {
        if(conf instanceof SomeConfig){return conf;
        conf = new SomeConfig();
        return conf;
    }
    
    this is called a factory method, for now this snippet doesn't look very much usefull but believe me it will.

    a good bridge of knowledge I use for static is Immutable objects, these objects are the best objects to be static only if it is needed.
    however if you use a non immutable object which could change things inside the internals such like a ItemStack for example then you can better not use it because it can cause unpredictable errors in separated threads.

    now why are these factory methods good?
    I use it to instance classes for just once from the main class and then make a getter and deleter to edit it.
    the idea is sometimes objects aren't immutable in that case you can write a wrapper holding the ItemStack and put the wrapper in a singleton HashMap or HashSet this means you do a attempt of making it abstract in a sort of sandbox which makes you profit when you place this in a factory method.

    I hope ive explained good, I'm on a iphone to so I can't reread.
     
    jthort likes this.
  19. Offline

    AronTheGamer

    @xTrollDudex It was like 3 oclock at night and o was very tired and made loads of spelling mistakes. So...
     
  20. Offline

    AronTheGamer

    I'm sorry... (ElmentAnimation villager sound)
    I know the conventions, and in my private/public work, I always use the right CamelCase, but it was late and didn't want to stay up till over 3:00pm to fix it ;)
     
    Garris0n likes this.
Thread Status:
Not open for further replies.

Share This Page