Solved Custom Configuration Files

Discussion in 'Plugin Development' started by MeZTech, Jan 4, 2014.

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

    MeZTech

    I need 3 configuration files for my plugin. So I followed the wiki's tutorial http://wiki.bukkit.org/Introduction...Reloading.2C_and_Saving_Custom_Configurations and made two classes. However, I keep getting this error when I load the plugin:
    http://pastebin.com/Zt9yahWy

    Main Class:
    Code:java
    1. package me.meztech;
    2.  
    3. import java.io.File;
    4. import java.util.List;
    5. import java.util.logging.Logger;
    6. import org.bukkit.Bukkit;
    7. import org.bukkit.command.Command;
    8. import org.bukkit.command.CommandSender;
    9. import org.bukkit.configuration.file.FileConfiguration;
    10. import org.bukkit.entity.Player;
    11. import org.bukkit.event.EventHandler;
    12. import org.bukkit.event.EventPriority;
    13. import org.bukkit.event.Listener;
    14. import org.bukkit.event.player.PlayerJoinEvent;
    15. import org.bukkit.plugin.java.JavaPlugin;
    16.  
    17. public class Main extends JavaPlugin implements Listener {
    18. // blacklist
    19. FileConfiguration blacklistConfig = null;
    20. File blacklistConfigFile = null;
    21. //options
    22. static FileConfiguration optionsConfig = null;
    23. static File optionsConfigFile = null;
    24.  
    25. public Logger logger = Logger.getLogger("minecraft");
    26. public FileConfiguration customConfig = null;
    27. public File customConfigFile = null;
    28.  
    29. public void onEnable() {
    30. logger.info("Nicknames: ENABLED");
    31. Bukkit.getPluginManager().registerEvents(this, this);
    32.  
    33. getConfig().options().copyDefaults(false);
    34. BlacklistFile.saveDefaultBlacklistConfig();
    35. OptionsConfig.saveOptionsConfig();
    36. }
    37.  
    38. public void onDisable() {
    39. logger.info("Nicknames: DISABLED");
    40. saveConfig();
    41. }
    42.  
    43. public boolean onCommand(CommandSender sender, Command cmd,
    44. String commandLabel, String args[]) {
    45. Player player = (Player) sender;
    46. // Main nickname command
    47. if (commandLabel.equalsIgnoreCase("nick")) {
    48. if (args.length == 1) {
    49. if (player.hasPermission("nickname.use")) {
    50. List<String> list = BlacklistFile.getBlacklistConfig().getStringList(
    51. "blacklist");
    52. if (list.contains(args[0])) {
    53. player.sendMessage("§b[§eNicknames§b] §bNickname not allowed.");
    54. } else {
    55. if (args[0].length() > OptionsConfig.getOptionsConfig()
    56. .getInt("nickname-character-limit")) {
    57. player.sendMessage(ErrorLibrary.overCharacterLimit);
    58. } else {
    59. player.setDisplayName(args[0].replaceAll("&", "§")
    60. + "§f"
    61. + OptionsConfig.getOptionsConfig()
    62. .getString("nickname-indicator"));
    63. player.sendMessage("§b[§eNicknames§b] §bNickname changed to: §e"
    64. + args[0]);
    65. getConfig().set(
    66. "nicknames." + player.getName() + ".nick",
    67. args[0] + "&f");
    68. getConfig().set(
    69. "nicknames." + player.getName() + ".real",
    70. player.getName());
    71. }
    72. }
    73. } else {
    74. player.sendMessage("§b[§eNicknames§b] §bNot enough permissions!");
    75. }
    76. } else {
    77. player.sendMessage("§b[§eNicknames§b] §bNot enough arguments!");
    78. player.sendMessage("§b[§eNicknames§b] §bCorrect usage: §e/nick <nickname>");
    79. }
    80.  
    81. // Turn off a nickname
    82. } else if (commandLabel.equalsIgnoreCase("nickoff")
    83. && player.hasPermission("nickname.use")) {
    84. player.sendMessage("§b[§eNicknames§b] §bNickname has been removed.");
    85. player.setDisplayName(player.getName());
    86. getConfig().set("nicknames." + player.getName() + ".nick",
    87. player.getName() + "&f");
    88. getConfig().set("nicknames." + player.getName() + ".real",
    89. player.getName());
    90.  
    91. // Set another player's nickname
    92. } else if (commandLabel.equalsIgnoreCase("nicko")) {
    93. if (player.hasPermission("nickname.use.other")) {
    94. if (args.length == 1 || args.length == 0) {
    95. player.sendMessage("§b[§eNicknames§b] §bNot enough arguments.");
    96. player.sendMessage("§b[§eNicknames§b] §bCorrect usage: §e/nicko <player> <nickname>");
    97. }
    98. if (args.length > 2) {
    99. player.sendMessage("§b[§eNicknames§b] §bNot enough arguments.");
    100. player.sendMessage("§b[§eNicknames§b] §bCorrect usage: §e/nicko <player> <nickname>");
    101. } else {
    102. List<String> list = BlacklistFile.getBlacklistConfig().getStringList(
    103. "blacklist");
    104. if (list.contains(args[0])) {
    105. player.sendMessage("§b[§eNicknames§b] §bNickname not allowed.");
    106. } else {
    107. if (args[1].length() > OptionsConfig.getOptionsConfig()
    108. .getInt("nickname-character-limit")) {
    109. player.sendMessage(ErrorLibrary.overCharacterLimit);
    110. } else {
    111. Player other = Bukkit.getServer()
    112. .getPlayer(args[0]);
    113. other.setDisplayName(args[1].replaceAll("&", "§")
    114. + "§f");
    115. getConfig().set(
    116. "nicknames." + other.getName() + ".nick",
    117. args[0] + "&f");
    118. getConfig().set(
    119. "nicknames." + other.getName() + ".real",
    120. other.getName());
    121. }
    122. }
    123. }
    124. }
    125. // Reload configuration file(S)
    126. } else if (commandLabel.equalsIgnoreCase("nickname reload")) {
    127. if (player.hasPermission("nickname.reload")) {
    128. // Reload blacklist
    129. BlacklistFile.reloadBlacklistConfig();
    130.  
    131. // reload options
    132. OptionsConfig.reloadOptionsConfig();
    133.  
    134. // reload nickname file
    135. reloadConfig();
    136. player.sendMessage("§b[§eNicknames§b] Configuration reloaded!");
    137. }
    138. }
    139. return false;
    140. }
    141.  
    142. @EventHandler(priority = EventPriority.NORMAL)
    143. public void onPlayerJoinEvent(PlayerJoinEvent event) {
    144. Player player = event.getPlayer();
    145. player.setDisplayName(getConfig().getString(
    146. "nicknames." + player.getName() + ".nick").replaceAll("&", "§"));
    147. }
    148.  
    149.  
    150. }
    151.  


    Configuration class #1:
    Code:java
    1. package me.meztech;
    2.  
    3. import java.io.File;
    4. import java.io.IOException;
    5. import java.util.logging.Level;
    6. import org.bukkit.configuration.file.FileConfiguration;
    7.  
    8. public class BlacklistFile {
    9. //Loads configuration file
    10. static FileConfiguration blacklistConfig = null;
    11. static File blacklistConfigFile = null;
    12.  
    13. //Variables for constructor
    14. static Main main;
    15.  
    16. public BlacklistFile(Main main){
    17. BlacklistFile.main = main;
    18. }
    19.  
    20. public static FileConfiguration getBlacklistConfig() {
    21. if (blacklistConfig == null) {
    22. reloadBlacklistConfig();
    23. }
    24. return blacklistConfig;
    25. }
    26.  
    27. public static void saveBlacklistConfig() {
    28. if (blacklistConfig == null || blacklistConfigFile == null) {
    29. return;
    30. }
    31. try {
    32. getBlacklistConfig().save(blacklistConfigFile);
    33. } catch (IOException ex) {
    34. main.getLogger().log(Level.SEVERE,
    35. "Could not save config to " + blacklistConfigFile, ex);
    36. }
    37. }
    38.  
    39. public static void reloadBlacklistConfig() {
    40. if (blacklistConfig == null || blacklistConfigFile == null) {
    41. return;
    42. }
    43. try {
    44. getBlacklistConfig().save(blacklistConfigFile);
    45. } catch (IOException ex) {
    46. main.getLogger().log(Level.SEVERE, "Could not save config to " + blacklistConfigFile, ex);
    47. }
    48. }
    49.  
    50. public static void saveDefaultBlacklistConfig() {
    51. if (blacklistConfigFile == null) {
    52. blacklistConfigFile = new File(main.getDataFolder(), "blacklist.yml");
    53. }
    54. if (!blacklistConfigFile.exists()) {
    55. main.saveResource("blacklist.yml", false);
    56. }
    57. }
    58.  
    59. }


    Configuration class #2:
    Code:java
    1. package me.meztech;
    2.  
    3. import java.io.File;
    4. import java.io.IOException;
    5. import java.io.InputStream;
    6. import java.util.logging.Level;
    7.  
    8. import org.bukkit.configuration.file.FileConfiguration;
    9. import org.bukkit.configuration.file.YamlConfiguration;
    10.  
    11. public class OptionsConfig {
    12.  
    13. static Main main;
    14. static FileConfiguration optionsConfig = null;
    15. static File optionsConfigFile = null;
    16.  
    17. public OptionsConfig(Main main){
    18. OptionsConfig.main = main;
    19. }
    20.  
    21. //Main Configuration File
    22. public static FileConfiguration getOptionsConfig() {
    23. if (optionsConfig == null) {
    24. reloadOptionsConfig();
    25. }
    26. return optionsConfig;
    27. }
    28.  
    29. public static void saveOptionsConfig() {
    30. if (optionsConfig == null || optionsConfigFile == null) {
    31. return;
    32. }
    33. try {
    34. getOptionsConfig().save(optionsConfigFile);
    35. } catch (IOException ex) {
    36. main.getLogger().log(Level.SEVERE,
    37. "Could not save config to " + optionsConfigFile, ex);
    38. }
    39. }
    40.  
    41. public static void reloadOptionsConfig() {
    42. if (!optionsConfigFile.exists()) {
    43. optionsConfigFile = new File(main.getDataFolder(), "optionsConfig.yml");
    44. }
    45. optionsConfig = YamlConfiguration
    46. .loadConfiguration(optionsConfigFile);
    47.  
    48. // Look for defaults in the jar
    49. InputStream defConfigStream = main.getResource("optionsConfig.yml");
    50. if (defConfigStream != null) {
    51. YamlConfiguration defConfig = YamlConfiguration
    52. .loadConfiguration(defConfigStream);
    53. optionsConfig.setDefaults(defConfig);
    54. }
    55. }
    56.  
    57. public static void saveDefaultOptionsConfig() {
    58. if (optionsConfigFile == null) {
    59. optionsConfigFile = new File(main.getDataFolder(), "customConfig.yml");
    60. }
    61. if (!optionsConfigFile.exists()) {
    62. main.saveResource("customConfig.yml", false);
    63. }
    64. }
    65.  
    66. }
    67.  


    I have the default .yml files in the jar.

    Any help is extremely appreciated.

    I really would like some help with this.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 6, 2016
  2. Offline

    xTrollxDudex

    MeZTech
    You forgot to initialize the class. Put this in your onEnable and DELETE lines 34-35
    PHP:
    BlacklistFile bf = new BlacklistFile(this);
    bf.saveDefaultBlacklistConfig();
     
    OptionsConfig oc = new OptionsConfig(this);
    oc.saveDefaultOptionsConfig();
     
  3. Offline

    MeZTech

    Ok after I do that any command that uses those gives this error:
    Code:
    org.bukkit.command.CommandException: Unhandled exception executing command 'nick' in plugin Nickname v0.1.2
        at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
    
    Anyone?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 6, 2016
  4. Offline

    Failplease

    MeZTech
    Show us the PluginCommand class.
     
  5. Offline

    DevVersion

    Code:java
    1. File f = new File("./plugins/YOURPLUGIN/config2.yml");
    2. YamlConfiguration config2 = FileConfiguration.load(f);


    Should Work
     
  6. Offline

    MeZTech

    The PluginCommand class is part of the bukkit API...

    Code:java
    1. package org.bukkit.command;
    2.  
    3. import java.util.List;
    4.  
    5. import org.apache.commons.lang.Validate;
    6. import org.bukkit.plugin.Plugin;
    7.  
    8. /**
    9. * Represents a {@link Command} belonging to a plugin
    10. */
    11. public final class PluginCommand extends Command implements PluginIdentifiableCommand {
    12. private final Plugin owningPlugin;
    13. private CommandExecutor executor;
    14. private TabCompleter completer;
    15.  
    16. protected PluginCommand(String name, Plugin owner) {
    17. super(name);
    18. this.executor = owner;
    19. this.owningPlugin = owner;
    20. this.usageMessage = "";
    21. }
    22.  
    23. /**
    24.   * Executes the command, returning its success
    25.   *
    26.   * @param sender Source object which is executing this command
    27.   * @param commandLabel The alias of the command used
    28.   * @param args All arguments passed to the command, split via ' '
    29.   * @return true if the command was successful, otherwise false
    30.   */
    31. @Override
    32. public boolean execute(CommandSender sender, String commandLabel, String[] args) {
    33. boolean success = false;
    34.  
    35. if (!owningPlugin.isEnabled()) {
    36. return false;
    37. }
    38.  
    39. if (!testPermission(sender)) {
    40. return true;
    41. }
    42.  
    43. try {
    44. success = executor.onCommand(sender, this, commandLabel, args);
    45. } catch (Throwable ex) {
    46. throw new CommandException("Unhandled exception executing command '" + commandLabel + "' in plugin " + owningPlugin.getDescription().getFullName(), ex);
    47. }
    48.  
    49. if (!success && usageMessage.length() > 0) {
    50. for (String line : usageMessage.replace("<command>", commandLabel).split("\n")) {
    51. sender.sendMessage(line);
    52. }
    53. }
    54.  
    55. return success;
    56. }
    57.  
    58. /**
    59.   * Sets the {@link CommandExecutor} to run when parsing this command
    60.   *
    61.   * @param executor New executor to run
    62.   */
    63. public void setExecutor(CommandExecutor executor) {
    64. this.executor = executor == null ? owningPlugin : executor;
    65. }
    66.  
    67. /**
    68.   * Gets the {@link CommandExecutor} associated with this command
    69.   *
    70.   * @return CommandExecutor object linked to this command
    71.   */
    72. public CommandExecutor getExecutor() {
    73. return executor;
    74. }
    75.  
    76. /**
    77.   * Sets the {@link TabCompleter} to run when tab-completing this command.
    78.   * <p>
    79.   * If no TabCompleter is specified, and the command's executor implements
    80.   * TabCompleter, then the executor will be used for tab completion.
    81.   *
    82.   * @param completer New tab completer
    83.   */
    84. public void setTabCompleter(TabCompleter completer) {
    85. this.completer = completer;
    86. }
    87.  
    88. /**
    89.   * Gets the {@link TabCompleter} associated with this command.
    90.   *
    91.   * @return TabCompleter object linked to this command
    92.   */
    93. public TabCompleter getTabCompleter() {
    94. return completer;
    95. }
    96.  
    97. /**
    98.   * Gets the owner of this PluginCommand
    99.   *
    100.   * @return Plugin that owns this command
    101.   */
    102. public Plugin getPlugin() {
    103. return owningPlugin;
    104. }
    105.  
    106. /**
    107.   * {@inheritDoc}
    108.   * <p>
    109.   * Delegates to the tab completer if present.
    110.   * <p>
    111.   * If it is not present or returns null, will delegate to the current
    112.   * command executor if it implements {@link TabCompleter}. If a non-null
    113.   * list has not been found, will default to standard player name
    114.   * completion in {@link
    115.   * Command#tabComplete(CommandSender, String, String[])}.
    116.   * <p>
    117.   * This method does not consider permissions.
    118.   *
    119.   * @throws CommandException if the completer or executor throw an
    120.   * exception during the process of tab-completing.
    121.   * @throws IllegalArgumentException if sender, alias, or args is null
    122.   */
    123. @Override
    124. public java.util.List<String> tabComplete(CommandSender sender, String alias, String[] args) throws CommandException, IllegalArgumentException {
    125. Validate.notNull(sender, "Sender cannot be null");
    126. Validate.notNull(args, "Arguments cannot be null");
    127. Validate.notNull(alias, "Alias cannot be null");
    128.  
    129. List<String> completions = null;
    130. try {
    131. if (completer != null) {
    132. completions = completer.onTabComplete(sender, this, alias, args);
    133. }
    134. if (completions == null && executor instanceof TabCompleter) {
    135. completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args);
    136. }
    137. } catch (Throwable ex) {
    138. StringBuilder message = new StringBuilder();
    139. message.append("Unhandled exception during tab completion for command '/").append(alias).append(' ');
    140. for (String arg : args) {
    141. message.append(arg).append(' ');
    142. }
    143. message.deleteCharAt(message.length() - 1).append("' in plugin ").append(owningPlugin.getDescription().getFullName());
    144. throw new CommandException(message.toString(), ex);
    145. }
    146.  
    147. if (completions == null) {
    148. return super.tabComplete(sender, alias, args);
    149. }
    150. return completions;
    151. }
    152.  
    153. @Override
    154. public String toString() {
    155. StringBuilder stringBuilder = new StringBuilder(super.toString());
    156. stringBuilder.deleteCharAt(stringBuilder.length() - 1);
    157. stringBuilder.append(", ").append(owningPlugin.getDescription().getFullName()).append(')');
    158. return stringBuilder.toString();
    159. }
    160. }


    Still not working.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 6, 2016
  7. Offline

    nichiatu

    MeZTech What does your onCommand method look like? :) That's most likely what DevVersion meant.
    Also, are the commands you're handling registered in your plugin.yml file?
     
  8. Offline

    MeZTech

    Yeah, that was the first thing I checked.

    onCommand():
    Code:java
    1. public boolean onCommand(CommandSender sender, Command cmd,
    2. String commandLabel, String[] args) {
    3. Player player = (Player) sender;
    4.  
    5. if(cmd.getName().equalsIgnoreCase("nick")){
    6. if(player.hasPermission("nickname.use")){
    7. if(args.length == 1){
    8. List<String> list = BlacklistFile.getBlacklistConfig().getStringList("blacklist");
    9. if(list.contains(args[0])){
    10. player.sendMessage(ErrorLibrary.nicknameNotAllowed);
    11. } else {
    12. if(args[0].length() > OptionsConfig.getOptionsConfig().getInt("nicknamecharacterlimit")){
    13. player.sendMessage(ErrorLibrary.overCharacterLimit);
    14. } else {
    15. player.setDisplayName(args[0].replaceAll("&", "§"));
    16. getConfig().set("nicknames." + player.getName() + ".nick", args[0]);
    17. getConfig().set("nicknames." + player.getName() + ".real", player.getName());
    18. }
    19. }
    20. } else {
    21. player.sendMessage(ErrorLibrary.notEnoughArguments);
    22. player.sendMessage(ErrorLibrary.nickCorrectUsage);
    23. }
    24. } else {
    25. player.sendMessage(ErrorLibrary.notEnoughPermissions);
    26. }
    27. }
    28. else if(commandLabel.equalsIgnoreCase("nickoff")){
    29. if(player.hasPermission("nickname.use")){
    30. if(args.length != 0){
    31. player.sendMessage(ErrorLibrary.notEnoughArguments);
    32. } else {
    33. player.setDisplayName(player.getName());
    34. getConfig().set("nicknames." + player.getName() + ".nick", player.getName());
    35. getConfig().set("nicknames." + player.getName() + ".real", player.getName());
    36. }
    37. }
    38. }
    39.  
    40. return false;
    41. }
     
  9. Offline

    Sagacious_Zed Bukkit Docs

    MeZTech
    You should give the entire error. Because the rest of the stack trace should have stated what was the unhanded exception in nick
     
  10. Offline

    MeZTech

    Sorry about that. I didn't think the rest would be useful.

    Code:
    [09:09:32 ERROR]: null
    org.bukkit.command.CommandException: Unhandled exception executing command 'nick' in plugin Nickname v0.1.2
        at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:196) ~[cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at org.bukkit.craftbukkit.v1_7_R1.CraftServer.dispatchCommand(CraftServer.java:542) ~[cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.PlayerConnection.handleCommand(PlayerConnection.java:929) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.PlayerConnection.a(PlayerConnection.java:811) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.PacketPlayInChat.a(PacketPlayInChat.java:28) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.PacketPlayInChat.handle(PacketPlayInChat.java:47) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.NetworkManager.a(NetworkManager.java:146) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.ServerConnection.c(SourceFile:134) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.MinecraftServer.u(MinecraftServer.java:655) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.DedicatedServer.u(DedicatedServer.java:250) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.MinecraftServer.t(MinecraftServer.java:545) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.MinecraftServer.run(MinecraftServer.java:457) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        at net.minecraft.server.v1_7_R1.ThreadServerApplication.run(SourceFile:617) [cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
    Caused by: java.lang.NullPointerException
        at me.meztech.nickname.Main.onCommand(Main.java:63) ~[?:?]
        at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[cb.jar:git-Bukkit-1.7.2-R0.1-1-gd791c11-b2970jnks]
        ... 13 more
    
     
  11. Offline

    Sagacious_Zed Bukkit Docs

    MeZTech Now if you read the Caused By line, you will obverse that it is just another run of the mill NullPointerException in Main.java
     
  12. Offline

    MeZTech

    Line 63 is getting a string from the BlacklistFile.
     
  13. Offline

    Sagacious_Zed Bukkit Docs

    That is what you would want it to do. What is actually happening when that line is executed is different (probably because of surrounding state), which results in throwing a NullPointerException.
     
  14. Offline

    MeZTech

    Ok so there is an error with the way I'm loading the separate classes, or an error with the methods themselves. Right now I'm using non-static methods and variables with a constructor to get the instance of my main class, so to get the files, I have to make a new instance of the class.
     
  15. Offline

    Sagacious_Zed Bukkit Docs

    This is a good question, and one you will have to answer yourself by examining your source code. Given that the plugin is in state that cause it to throw an NPE. NullPointerExceptions are thrown when very specific circumstances are met. Once you figure out the circumstance it happens, will need to take some step to mitigate those situations.
     
Thread Status:
Not open for further replies.

Share This Page