[Tut] How to make super awesome player wrapper

Discussion in 'Resources' started by beastman3226, Oct 21, 2013.

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

    beastman3226

    Hey guys! I have noticed a growing need to add cool methods to player without having to write code to implement the player interface and the implicit issue of making sure the JVM realizes all those players are players on the server aswell. The player wrapper is a super awesome tool that when done right works amazingly.

    A wrapper is an object that takes a type or object and then adds things to it. For example:
    Code:java
    1. class MyWrapper {
    2. private Player wrappedPlayer;
    3. }

    We need this player object so we know what player we are referring to later on down the road.

    Let's add some functionality to this player. We are going to create a method say hi, we are going to keep the class above.
    Code:java
    1. //class and field declaration
    2. public void myNewFunction() {
    3. player.say("hi");
    4. player.sendMessage("Haha! you have no choice but to say hi!!");
    5. }

    This is a very simple wrapper that adds a function that forces the player to say hi. You can do a lot more with this. You can add attributes to players like so:
    Adding Attributes (open)

    Code:java
    1. //class and field declaration
    2. private long timePlayed;
    3. public long getTimePlayed() {
    4. return this.timePlayed;
    5. }
    6. //we are going to assume we are passing in a stored value from System.getCurrentMillis()
    7. public long addTimePlayed(long loginTime, long logoutTime) {
    8. this.timePlayed += (logoutTime - loginTime);
    9. return timePlayed;
    10. }


    If you read you could tell we gave our player a time played attribute. If you wanted to get fancier you could also do custom quests and what not.

    The last thing we need to do is allow the player to get garbage collected. We are going to do this by setting the player to null. (Thanks to Cirno for bringing this to my attention)
    Final Class (open)
    Code:java
    1. package me.beastman3226.example;
    2.  
    3. import org.bukkit.entity.Player;
    4. import org.bukkit.event.EventHandler;
    5. import org.bukkit.event.Listener;
    6. import org.bukkit.event.player.PlayerJoinEvent;
    7. import org.bukkit.event.player.PlayerQuitEvent;
    8. import org.bukkit.event.server.PluginEnableEvent;
    9.  
    10. public class WrappedPlayer {
    11.  
    12. private Player player;
    13. private String playerName;
    14. public static WrappedPlayer[] players;
    15. public static Main plugin;
    16.  
    17. public Player getPlayer() {
    18. return player;
    19. }
    20.  
    21. public void setPlayer(Player player) {
    22. this.player = player;
    23. }
    24.  
    25. public WrappedPlayer(Player p) {
    26. this.player = p;
    27. players[findOpen()] = this;
    28. }
    29.  
    30. public String getPlayerName() {
    31. return playerName;
    32. }
    33.  
    34. public void setPlayerName() {
    35. this.playerName = this.getPlayer().getName();
    36. }
    37.  
    38. private int findOpen() {
    39. int i = 0;
    40. while(players.length > i) {
    41. if(players[i] == null) {
    42. return i;
    43. } else {
    44. i++;
    45. continue;
    46. }
    47. }
    48. return 0;
    49. }
    50.  
    51. public static class PlayerListener implements Listener {
    52. @EventHandler
    53. public void onJoin(PlayerJoinEvent e) {
    54. if(!WrappedPlayer.contains(e.getPlayer())) new WrappedPlayer(e.getPlayer());
    55. }
    56. @EventHandler
    57. public void onLeave(PlayerQuitEvent e) {
    58. WrappedPlayer.getPlayer(e.getPlayer()).setPlayer(null);
    59. }
    60. @EventHandler
    61. public void onStart(PluginEnableEvent e) {
    62. if(e.getPlugin() == plugin) {
    63. players = new WrappedPlayer[plugin.getMax()];
    64. }
    65. }
    66. }
    67.  
    68. public static boolean contains(Player player) {
    69. for(WrappedPlayer pw : players) {
    70. if(pw.getPlayer() == player) {
    71. return true;
    72. }
    73. }
    74. return false;
    75. }
    76.  
    77. public static WrappedPlayer getPlayer(Player player2) {
    78. for(WrappedPlayer player : players) {
    79. if(player.getPlayer() == player2) {
    80. return player;
    81. } else {
    82. continue;
    83. }
    84. }
    85. return null;
    86. }
    87.  
    88.  
    89. }[/i]


    In summary, we refer to a player object so that we can still change things from the wrapper. We can add attributes by adding the field and adding the necessary functions. Please feel free to suggest things and ask questions.
     
  2. Offline

    Cirno

    It would be wise to make wrappedPlayer nonfinal and upon PlayerQuit, set wrappedPlayer to null to allow the JVM to GC wrappedPlayer.
     
  3. Offline

    beastman3226

    Cirno
    Thank you. I forgot to mention that.
     
  4. Offline

    SacredWaste

    Should improve getPlayer() function, consumes too much memory.
     
  5. Offline

    Ultimate_n00b

    I have a full player wrapper that doesn't implement player lying around here somewhere.. give me a sec to find it.
     
    LegoPal92 likes this.
  6. Offline

    beastman3226

    SacredWaste
    I was thinking about changing the get player from the actual player to a name. That way I wouldn't have to worry about the listeners. How does returning the stored value take up too much memory?
     
  7. Offline

    TheGamersCave

    This isn't a very efficient way to create a player wrapper :/
    You're storing the actual player object which can & will lead to issues; I suggest storing the players name; Have a method to get the name, along with a .getPlayer() method that calls Bukkit.getPlayer with the instanced name.

    Great starting point though, I will say that!

    Here's an example of a wrapper I wrote (for a minigame, still the same concept though):
    Code:java
    1. package TDMGame.Handlers.Fakeboard;
    2.  
    3. import java.io.File;
    4. import java.util.ArrayList;
    5. import java.util.Arrays;
    6. import java.util.HashMap;
    7. import java.util.List;
    8.  
    9. import org.bukkit.Bukkit;
    10. import org.bukkit.ChatColor;
    11. import org.bukkit.entity.Player;
    12. import org.bukkit.inventory.ItemStack;
    13. import org.bukkit.potion.PotionEffect;
    14. import org.bukkit.potion.PotionEffectType;
    15.  
    16. import TDMGame.TDMGame;
    17. import TDMGame.TDMGame.LoadoutSlot;
    18. import TDMGame.Handlers.FileHandlers.DataHandler;
    19. import TDMGame.Handlers.Gunhandlers.GunWrap;
    20. import TDMGame.Handlers.Loadout.Loadout;
    21. import TDMGame.Handlers.Perks.*;
    22. import TDMGame.Handlers.Perks.Perks.Nothing;
    23. import TDMGame.Handlers.Scoreboard.PlayerScoreboard;
    24.  
    25. public class fPlayer
    26. {
    27. private String Name = "";
    28. private String TeamName = "";
    29.  
    30. private int Score = 0;
    31. private int TeamKills = 0;
    32. private int KillStreak = 0;
    33. private int Deaths = 0;
    34.  
    35. private String PrimaryGunID = "AK-47";
    36. private String SecondaryGunID = "USP45";
    37.  
    38. private PlayerScoreboard Scoreboard;
    39.  
    40. private Perk Perk;
    41.  
    42. private boolean IsAfk = false;
    43.  
    44. private List<Perk> Perks = new ArrayList<Perk>();
    45.  
    46. private int SelectedLoadout = 1;
    47.  
    48. //private List<LoadoutConfig> Loadouts = new ArrayList<LoadoutConfig>();
    49. private HashMap<Integer, Loadout> Loadouts = new HashMap<Integer, Loadout>();
    50.  
    51. private List<String> UnlockedGuns = new ArrayList<String>();
    52.  
    53. private List<ItemStack> DeathRestoration = new ArrayList<ItemStack>();
    54. private ItemStack[] DeathInventory = new ItemStack[] { };
    55.  
    56. /**
    57.   * Initiates a new fPlayer Instance
    58.   * @param Player Players Name
    59.   */
    60. public fPlayer(String Player)
    61. {
    62. this.Name = Player;
    63. this.Perk = new Nothing();
    64. Defaults();
    65. Setup();
    66. }
    67.  
    68. /**
    69.   * Initiates a new fPlayer Instance
    70.   * @param Player Player to make new fPlayer of
    71.   */
    72. public fPlayer(Player Player)
    73. {
    74. this.Name = Player.getName();
    75. this.Perk = new Nothing();
    76. Defaults();
    77. Setup();
    78. }
    79.  
    80. public void Defaults()
    81. {
    82. if (TDMGame.LoadoutSQL.getLoadouts(this.Name).size() <= 0 || TDMGame.LoadoutSQL.getLoadouts(this.Name).size() < this.getLoadoutLimit())
    83. {
    84. for(int I = 0; I < this.getLoadoutLimit(); I++)
    85. {
    86. TDMGame.LoadoutSQL.InsertLoadout(this.Name, (I + 1), this.PrimaryGunID, this.SecondaryGunID, this.Perk.getName());
    87. }
    88. }
    89.  
    90. if (TDMGame.GunsSQL.getGuns(this.Name).size() < TDMGame.GunHandler.getDefaultGuns().size())
    91. {
    92. for(GunWrap Gun : TDMGame.GunHandler.getDefaultGuns())
    93. {
    94. TDMGame.GunsSQL.InsertGun(this.Name, Gun.getName());
    95. }
    96. }
    97.  
    98. if (TDMGame.PerkSQL.getPerks(this.Name).size() <= 0)
    99. {
    100. TDMGame.PerkSQL.InsertPerk(new Nothing(), this.Name);
    101. }
    102. }
    103.  
    104. public void Setup()
    105. {
    106. for(Loadout Loadout : TDMGame.LoadoutSQL.getLoadouts(this.Name))
    107. {
    108. this.Loadouts.put(Loadout.getNumber(), Loadout);
    109. }
    110.  
    111. for(Perk Perk : TDMGame.PerkSQL.getPerks(this.Name))
    112. {
    113. this.Perks.add(Perk);
    114. }
    115.  
    116. for(String Gun : TDMGame.GunsSQL.getGuns(this.Name))
    117. {
    118. this.UnlockedGuns.add(Gun);
    119. }
    120. this.Scoreboard = new PlayerScoreboard();
    121. if (Bukkit.getPlayer(this.Name) != null)
    122. {
    123. Bukkit.getPlayer(this.Name).setScoreboard(this.getScoreboard().getScoreboard());
    124. }
    125. }
    126.  
    127.  
    128. public PlayerScoreboard getScoreboard()
    129. {
    130. return this.Scoreboard;
    131. }
    132.  
    133. public void UpdateScoreboard()
    134. {
    135. this.Scoreboard.UpdateData(this);
    136. }
    137.  
    138. public void ClearScoreboard()
    139. {
    140. this.Scoreboard.ClearBoard();
    141. }
    142.  
    143. public List<String> getUnlockedGuns()
    144. {
    145. return this.UnlockedGuns;
    146. }
    147.  
    148. public boolean hasGun(String ID)
    149. {
    150. return this.UnlockedGuns.contains(ID);
    151. }
    152.  
    153. public boolean hasGun(GunWrap Gun)
    154. {
    155. return this.UnlockedGuns.contains(Gun.getName());
    156. }
    157.  
    158. public void UnlockGun(String ID)
    159. {
    160. if (!this.UnlockedGuns.contains(ID))
    161. {
    162. this.UnlockedGuns.add(ID);
    163. TDMGame.GunsSQL.InsertGun(this.Name, ID);
    164. TDMGame.Console("Unlocked gun [" + ID + "] in SQL for " + this.Name);
    165. }
    166. }
    167.  
    168.  
    169. public int getLoadoutLimit()
    170. {
    171. if (this.getPlayer().isWhitelisted())
    172. {
    173. return 9;
    174. }
    175. else
    176. {
    177. return 3;
    178. }
    179. }
    180.  
    181. public List<Perk> getPerks()
    182. {
    183. return this.Perks;
    184. }
    185.  
    186. public boolean hasPerk(Perk Perk)
    187. {
    188. return this.Perks.contains(Perk);
    189. }
    190.  
    191. public void addPerk(Perk Perk)
    192. {
    193. if (!this.Perks.contains(Perk))
    194. {
    195. this.Perks.add(Perk);
    196. TDMGame.PerkSQL.InsertPerk(Perk, this.Name);
    197. TDMGame.Console("Unlocked Perk [" + Perk.getName() + "] in SQL for " + this.Name);
    198. }
    199. }
    200.  
    201. /**
    202.   * Gets the fPlayers Name
    203.   * @return
    204.   */
    205. public String getName()
    206. {
    207. return this.Name;
    208. }
    209.  
    210. /**
    211.   * Set the name of the fPlayer
    212.   * @param Name
    213.   */
    214. public void setName(String Name)
    215. {
    216. this.Name = Name;
    217. }
    218.  
    219. /**
    220.   * Gets the Player for the fPlayer in this instance; Uses Bukkit.getPlayer
    221.   * @return
    222.   */
    223. public Player getPlayer()
    224. {
    225. if (Bukkit.getPlayer(this.Name) != null)
    226. {
    227. return Bukkit.getPlayer(this.Name);
    228. }
    229. return null;
    230. }
    231.  
    232. /**
    233.   * Gets this fPlayers Score
    234.   * @return
    235.   */
    236. public int getScore()
    237. {
    238. return this.Score;
    239. }
    240.  
    241. /**
    242.   * Sets the score for this fPlayer
    243.   * @param Score
    244.   */
    245. public void setScore(int Score)
    246. {
    247. this.Score = Score;
    248. }
    249.  
    250. /**
    251.   * Add more to the fPlayers Score
    252.   * @param Amount
    253.   */
    254. public void addScore(int Amount)
    255. {
    256. this.Score += Amount;
    257. }
    258.  
    259. /**
    260.   * Set the team this fPlayer is on
    261.   * @param Name
    262.   */
    263. public void setTeam(String Name)
    264. {
    265. this.TeamName = Name;
    266. }
    267.  
    268. /**
    269.   * Gets the name of the team this fPlayer is on
    270.   * @return
    271.   */
    272. public String getTeam()
    273. {
    274. return this.TeamName;
    275. }
    276.  
    277. /**
    278.   * Gets how many Team-Kills this fPlayer has
    279.   * @return
    280.   */
    281. public int getTeamKills()
    282. {
    283. return this.TeamKills;
    284. }
    285.  
    286. /**
    287.   * Adds to the Team-Kills this player has
    288.   * @param Amount
    289.   */
    290. public void addTeamKills(int Amount)
    291. {
    292. this.TeamKills += Amount;
    293. }
    294.  
    295. /**
    296.   * Gets how many kills this fPlayer has on their current Killstreak
    297.   * @return
    298.   */
    299. public int getKillStreak()
    300. {
    301. return this.KillStreak;
    302. }
    303.  
    304. /**
    305.   * Sets the players Killstreak to 0
    306.   */
    307. public void ResetKillstreak()
    308. {
    309. this.KillStreak = 0;
    310. }
    311.  
    312. /**
    313.   * Adds the Amount to this players Killstreak
    314.   * @param Amount
    315.   */
    316. public void AddKillStreak(int Amount)
    317. {
    318. this.KillStreak += Amount;
    319. }
    320.  
    321. /**
    322.   * Gets this players Chosen gun
    323.   * @return
    324.   */
    325. public String getPrimaryGunID()
    326. {
    327. return this.PrimaryGunID;
    328. }
    329.  
    330. /**
    331.   * Returns the secondary gun in the players selected loadout
    332.   * @return
    333.   */
    334. public String getSecondaryGunID()
    335. {
    336. return this.SecondaryGunID;
    337. }
    338.  
    339. public String getPrimaryGunID(int Loadout)
    340. {
    341. return this.getLoadout(Loadout).getPrimary();
    342. }
    343.  
    344. public String getSecondaryGunID(int Loadout)
    345. {
    346. return this.getLoadout(Loadout).getSecondary();
    347. }
    348.  
    349. public Perk getPerk(int Loadout)
    350. {
    351. return this.getLoadout(Loadout).getPerk();
    352. }
    353.  
    354. public void setActiveLoadout(int LoadoutNumber)
    355. {
    356. if (this.getLoadout(LoadoutNumber) != null)
    357. {
    358. this.SelectedLoadout = LoadoutNumber;
    359. this.PrimaryGunID = this.getLoadout(LoadoutNumber).getPrimary();
    360. this.SecondaryGunID = this.getLoadout(LoadoutNumber).getSecondary();
    361. this.Perk = this.getLoadout(LoadoutNumber).getPerk();
    362. }
    363. }
    364.  
    365. /**
    366.   * Gets this players inventory to restore after death
    367.   * @return
    368.   */
    369. public List<ItemStack> getDeathInventoryAsList()
    370. {
    371. return this.DeathRestoration;
    372. }
    373.  
    374. /**
    375.   * Gets this players inventory to restore after death in an ItemStack[]
    376.   * @return
    377.   */
    378. public ItemStack[] getDeathInventory()
    379. {
    380. return this.DeathInventory;
    381. }
    382.  
    383. /**
    384.   * Sets the items that this player will get after death
    385.   * @param ItemsDiedWith
    386.   */
    387. public void setDeathInventory(List<ItemStack> ItemsDiedWith)
    388. {
    389. this.DeathRestoration = ItemsDiedWith;
    390. }
    391.  
    392. /**
    393.   * Sets the items that this player will get after death
    394.   * @param ItemsDiedWith
    395.   */
    396. public void setDeathInventory(ItemStack[] ItemsDiedWith)
    397. {
    398. this.DeathRestoration = Arrays.asList(ItemsDiedWith);
    399. this.DeathInventory = ItemsDiedWith;
    400. }
    401.  
    402. /**
    403.   * Clears the inventory data for this player
    404.   */
    405. public void clearDeathInventory()
    406. {
    407. this.DeathRestoration.clear();
    408. this.DeathInventory = new ItemStack[] { };
    409. }
    410.  
    411. public Loadout getLoadout(int Number)
    412. {
    413. if (this.Loadouts.containsKey(Number))
    414. {
    415. return this.Loadouts.get(Number);
    416. }
    417. else
    418. {
    419. return null;
    420. }
    421. }
    422.  
    423. public void setAfk(boolean IsAFK)
    424. {
    425. if (IsAFK == true)
    426. {
    427. this.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,(20 * 60) * 10, 1));
    428. }
    429. else
    430. {
    431. this.getPlayer().removePotionEffect(PotionEffectType.INVISIBILITY);
    432. }
    433. this.IsAfk = IsAFK;
    434. this.getPlayer().sendMessage(ChatColor.GRAY + "You are" + (this.isAfk() ? " now afk" : " no longer afk"));
    435. }
    436.  
    437. public boolean isAfk()
    438. {
    439. return this.IsAfk;
    440. }
    441.  
    442. public Perk getPerk()
    443. {
    444. return this.Perk;
    445. }
    446.  
    447. public int getDeaths()
    448. {
    449. return this.Deaths;
    450. }
    451.  
    452. public void ResetDeaths()
    453. {
    454. this.Deaths = 0;
    455. }
    456.  
    457. public void AddDeath()
    458. {
    459. this.Deaths += 1;
    460. }
    461.  
    462. }
    463.  


    I know that the formatting isn't lowerCamelCasing, but the concept of the wrapper is still there: It holds wraps data for the player.
     
  8. Offline

    beastman3226

    I know, I started making an example project on Github and will post later once I am finished.
     
  9. Offline

    SacredWaste

    Using Player like that in a wrapper has many bugs, the main one being a memory leak.
     
  10. Offline

    beastman3226

    SacredWaste
    I understand. You could use Player, cast to OnlinePlayer when online and cast to OfflinePlayer when he quits. Or I can store the name. Either way it goes I am editing the topic and creating an example project.
     
  11. Offline

    SacredWaste

    The best way to prevent the memory leaks is to store String's instead, and use a getPlayer() method to retrieve the actual Player class by using the Bukkit.getPlayer() function.
     
  12. Offline

    beastman3226

  13. Offline

    confuserr

    getPlayer is slow, use getPlayerExact. There is absolutely nothing wrong with storing a reference to the player object as long as you remove it when the player leaves the server. Even if you don't use the player object, if you still don't remove this wrapper when the player leaves, you'll still have a memory leak regardless.
     
Thread Status:
Not open for further replies.

Share This Page