diff options
8 files changed, 208 insertions, 45 deletions
| diff --git a/options.txt b/options.txt index 200b71ef..1b8c369a 100755 --- a/options.txt +++ b/options.txt @@ -24,7 +24,7 @@ chatLinks:true  chatLinksPrompt:true  chatOpacity:1.0  snooperEnabled:true -fullscreen:true +fullscreen:false  enableVsync:false  useVbo:true  hideServerAddress:false @@ -116,7 +116,7 @@ key_Flip (Hold):0  key_Rotate (Hold):0  key_Flip (Toggle):0  key_Rotate (Toggle):0 -soundCategory_master:1.0 +soundCategory_master:0.0  soundCategory_music:0.0  soundCategory_record:1.0  soundCategory_weather:1.0 diff --git a/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java b/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java index 87ff13e2..c4e006bb 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java @@ -327,24 +327,18 @@ public class CommandDungeonsGuide extends CommandBase {                  sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §cAn error occured while writing rundata "+e.getMessage()));                  e.printStackTrace();              } -        } else if (args[0].equals("fetch")) { -            try { -                sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §eProfile Viewer Test: ").appendSibling(new ChatComponentText("§7view").setChatStyle(new ChatStyle().setChatHoverEvent(new FeatureViewPlayerOnJoin.HoverEventRenderPlayer(args[1]))))); -            } catch (Exception e) { -                e.printStackTrace(); -            } -        } else if (args[0].equals("fetchbynick")) { +        } else if (args[0].equals("pv")) {              try {                  ApiFetchur.fetchUUIDAsync(args[1])                          .thenAccept(a -> { -                            sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §eProfile Viewer Test: ").appendSibling(new ChatComponentText("§7view").setChatStyle(new ChatStyle().setChatHoverEvent(new FeatureViewPlayerOnJoin.HoverEventRenderPlayer(a.orElse(null)))))); +                            sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e"+args[1]+"§f's Profile ").appendSibling(new ChatComponentText("§7view").setChatStyle(new ChatStyle().setChatHoverEvent(new FeatureViewPlayerOnJoin.HoverEventRenderPlayer(a.orElse(null))))));                          }); -              } catch (Exception e) {                  e.printStackTrace();              }          } else if (args[0].equals("purge")) {              ApiFetchur.purgeCache(); +            sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §fSuccessfully purged API Cache!"));          } else {              sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg §7-§fOpens configuration gui"));              sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg gui §7-§fOpens configuration gui")); @@ -357,6 +351,7 @@ public class CommandDungeonsGuide extends CommandBase {              sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg info §7-§f View Current DG User info."));              sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg asktojoin or /dg atj §7-§f Toggle ask to join §cRequires Discord Rich Presence enabled. (/dg -> Advanced)"));              sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg partymax [number] or /dg pm [number] §7-§f Sets partymax §7(maximum amount people in party, for discord rpc)")); +            sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg pv [ign] §7-§f Profile Viewer"));          }      } diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java index b62a6764..bf2779d9 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java @@ -11,6 +11,7 @@ import kr.syeyoung.dungeonsguide.features.impl.dungeon.*;  import kr.syeyoung.dungeonsguide.features.impl.etc.*;  import kr.syeyoung.dungeonsguide.features.impl.etc.ability.FeatureAbilityCooldown;  import kr.syeyoung.dungeonsguide.features.impl.party.APIKey; +import kr.syeyoung.dungeonsguide.features.impl.party.FeatureGoodParties;  import kr.syeyoung.dungeonsguide.features.impl.party.playerpreview.FeatureViewPlayerOnJoin;  import kr.syeyoung.dungeonsguide.features.impl.secret.FeatureActions;  import kr.syeyoung.dungeonsguide.features.impl.secret.FeatureFreezePathfind; @@ -86,6 +87,7 @@ public class FeatureRegistry {      public static final APIKey PARTYKICKER_APIKEY = register(new APIKey());      public static final FeatureViewPlayerOnJoin PARTYKICKER_VIEWPLAYER = register(new FeatureViewPlayerOnJoin()); +    public static final FeatureGoodParties PARTYKICKER_GOODPARTIES = register(new FeatureGoodParties());      public static final FeatureWarningOnPortal BOSSFIGHT_WARNING_ON_PORTAL = register(new FeatureWarningOnPortal());      public static final SimpleFeature BOSSFIGHT_CHESTPRICE = register(new FeatureChestPrice()); diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/APIKey.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/APIKey.java index ed708ec5..8516944e 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/APIKey.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/APIKey.java @@ -1,9 +1,13 @@  package kr.syeyoung.dungeonsguide.features.impl.party;  import kr.syeyoung.dungeonsguide.features.FeatureParameter; +import kr.syeyoung.dungeonsguide.features.FeatureRegistry;  import kr.syeyoung.dungeonsguide.features.SimpleFeature;  import kr.syeyoung.dungeonsguide.features.listener.ChatListener;  import kr.syeyoung.dungeonsguide.features.listener.ChatListenerGlobal; +import kr.syeyoung.dungeonsguide.utils.TextUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ChatComponentText;  import net.minecraftforge.client.event.ClientChatReceivedEvent;  public class APIKey extends SimpleFeature implements ChatListenerGlobal { @@ -20,6 +24,13 @@ public class APIKey extends SimpleFeature implements ChatListenerGlobal {      @Override      public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) { -        // ay set apikey +        if (clientChatReceivedEvent.type == 2) return; +        String str = clientChatReceivedEvent.message.getFormattedText(); +        if (str.startsWith("§aYour new API key is §r§b")) { +            String apiKeys = TextUtils.stripColor(str.split(" ")[5]); +            Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §fAutomatically Configured Hypixel API Key")); +            this.<String>getParameter("apikey").setValue(apiKeys); +        } +      }  } diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureGoodParties.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureGoodParties.java new file mode 100644 index 00000000..ae7b0b45 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureGoodParties.java @@ -0,0 +1,93 @@ +package kr.syeyoung.dungeonsguide.features.impl.party; + +import kr.syeyoung.dungeonsguide.features.FeatureRegistry; +import kr.syeyoung.dungeonsguide.features.SimpleFeature; +import kr.syeyoung.dungeonsguide.features.impl.party.api.ApiFetchur; +import kr.syeyoung.dungeonsguide.features.impl.party.api.DungeonType; +import kr.syeyoung.dungeonsguide.features.impl.party.api.PlayerProfile; +import kr.syeyoung.dungeonsguide.features.listener.GuiPostRenderListener; +import kr.syeyoung.dungeonsguide.features.listener.TickListener; +import kr.syeyoung.dungeonsguide.utils.TextUtils; +import kr.syeyoung.dungeonsguide.utils.XPUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.inventory.GuiChest; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.init.Items; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.ContainerChest; +import net.minecraft.inventory.Slot; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.client.event.GuiScreenEvent; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class FeatureGoodParties extends SimpleFeature implements GuiPostRenderListener { +    public FeatureGoodParties() { +        super("Party Kicker", "Highlight parties in party viewer", "Highlight parties you can't join with red", "partykicker.goodparty",true); +    } + +    @Override +    public void onGuiPostRender(GuiScreenEvent.DrawScreenEvent.Post rendered) { +        if (!isEnabled()) return; +        if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChest)) return; +        GuiChest chest = (GuiChest) Minecraft.getMinecraft().currentScreen; +        ContainerChest cont = (ContainerChest) chest.inventorySlots; +        String name = cont.getLowerChestInventory().getName(); +        if (!"Party Finder".equals(name)) return; + + +        int i = 222; +        int j = i - 108; +        int ySize = j + (((ContainerChest)(((GuiChest) Minecraft.getMinecraft().currentScreen).inventorySlots)).getLowerChestInventory().getSizeInventory() / 9) * 18; +        int left = (rendered.gui.width - 176) / 2; +        int top = (rendered.gui.height - ySize ) / 2; +        GlStateManager.pushMatrix(); +        GlStateManager.disableDepth(); +        GlStateManager.disableLighting(); +        GlStateManager.colorMask(true, true, true, false); +        GlStateManager.translate(left, top, 0); +        FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; +        try { + +            for (int i1 = 0; i1 < Integer.min(54, cont.inventorySlots.size()); i1++) { +                Slot s = cont.inventorySlots.get(i1); +                if (s.getStack() == null) continue; +                if (s.getStack().getItem() != Items.skull) continue; +                NBTTagCompound nbt = s.getStack().getTagCompound(); +                if (nbt == null || nbt.hasNoTags()) continue; +                NBTTagCompound display = nbt.getCompoundTag("display"); +                if (display.hasNoTags()) return; +                NBTTagList lore = display.getTagList("Lore", 8); +                int classLvReq = 0; +                int cataLvReq = 0; +                boolean Req = false; +                for (int n = 0; n < lore.tagCount(); n++) { +                    String str = lore.getStringTagAt(n); +                    if (str.startsWith("§7Dungeon Level Required: §b")) cataLvReq = Integer.parseInt(str.substring(28)); +                    if (str.startsWith("§7Class Level Required: §b")) classLvReq = Integer.parseInt(str.substring(26)); +                    if (str.startsWith("§cRequires")) Req = true; +                } +                System.out.println(classLvReq + " / "+cataLvReq); + +                if (Req) { +                    int x = s.xDisplayPosition; +                    int y = s.yDisplayPosition; +                    Gui.drawRect(x, y, x + 16, y + 16, 0x77AA0000); +                } + + +            } +        } catch (Throwable e) { +            e.printStackTrace(); +        } +        GlStateManager.colorMask(true, true, true, true); +        GlStateManager.popMatrix(); +        GlStateManager.enableBlend(); +        GlStateManager.enableLighting(); +    } +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ApiFetchur.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ApiFetchur.java index 0a80d93c..2c22b94a 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ApiFetchur.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ApiFetchur.java @@ -29,6 +29,7 @@ public class ApiFetchur {      private static final ExecutorService ex = Executors.newFixedThreadPool(4); +    private static final Set<String> invalidKeys = new HashSet<>();      public static void purgeCache() {          playerProfileCache.clear();          nicknameToUID.clear(); @@ -39,6 +40,7 @@ public class ApiFetchur {          completableFutureMap2.clear();          completableFutureMap3.clear();          completableFutureMap4.clear(); +        invalidKeys.clear();      }      public static JsonObject getJson(String url) throws IOException { @@ -106,6 +108,11 @@ public class ApiFetchur {              playerProfileCache.remove(uid);          }          if (completableFutureMap.containsKey(uid)) return completableFutureMap.get(uid); +        if (invalidKeys.contains(apiKey)) { +            CompletableFuture cf = new CompletableFuture(); +            cf.completeExceptionally(new IOException("403 for url")); +            return cf; +        }          CompletableFuture<Optional<PlayerProfile>> completableFuture = new CompletableFuture<>();          ex.submit(() -> { @@ -116,10 +123,16 @@ public class ApiFetchur {                  completableFutureMap.remove(uid);                  return;              } catch (IOException e) { +                if (e.getMessage().contains("403 for URL")) { +                    completableFuture.completeExceptionally(e); +                    completableFutureMap.remove(uid); +                    invalidKeys.add(apiKey); +                } else { +                    completableFuture.complete(Optional.empty()); +                    completableFutureMap.remove(uid); +                }                  e.printStackTrace();              } -            completableFuture.complete(Optional.empty()); -            completableFutureMap.remove(uid);          });          completableFutureMap.put(uid, completableFuture);          return completableFuture; diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/playerpreview/FeatureViewPlayerOnJoin.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/playerpreview/FeatureViewPlayerOnJoin.java index 0e96d4c4..b2596f63 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/playerpreview/FeatureViewPlayerOnJoin.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/playerpreview/FeatureViewPlayerOnJoin.java @@ -20,6 +20,7 @@ import kr.syeyoung.dungeonsguide.features.listener.GuiPostRenderListener;  import kr.syeyoung.dungeonsguide.features.text.PanelTextParameterConfig;  import kr.syeyoung.dungeonsguide.features.text.TextHUDFeature;  import kr.syeyoung.dungeonsguide.gui.MPanel; +import kr.syeyoung.dungeonsguide.party.PartyManager;  import kr.syeyoung.dungeonsguide.utils.TextUtils;  import kr.syeyoung.dungeonsguide.utils.XPUtils;  import lombok.Getter; @@ -71,40 +72,41 @@ public class FeatureViewPlayerOnJoin extends SimpleFeature implements GuiPostRen      @Override      public void onGuiPostRender(GuiScreenEvent.DrawScreenEvent.Post rendered) {          if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChat)) { -            popupRect = null; -            profileFuture = null; -            gfFuture = null; -            skinFuture=  null; -            fakePlayer= null; +            cancelRender();              return;          }          ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft()); -        int width = scaledResolution.getScaledWidth(); -        int height = scaledResolution.getScaledHeight(); -        int mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth; -        int mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1; -          IChatComponent ichatcomponent = getHoveredComponent(scaledResolution);          String uid = null;          if (ichatcomponent != null && ichatcomponent.getChatStyle().getChatHoverEvent() instanceof HoverEventRenderPlayer) {              uid = ((HoverEventRenderPlayer) ichatcomponent.getChatStyle().getChatHoverEvent()).getUuid();          } +        reqRender(uid); +    } + +    public void cancelRender() { +        popupRect = null; +        profileFuture = null; +        lastuid = null; +        gfFuture = null; +        skinFuture=  null; +        fakePlayer= null; +    } + +    public void reqRender(String uid) { +        ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft()); +        int width = scaledResolution.getScaledWidth(); +        int height = scaledResolution.getScaledHeight(); +        int mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth; +        int mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1; +          if (!((popupRect != null && popupRect.contains(mouseX, mouseY)) || uid != null && uid.equals(lastuid))) { -            popupRect = null; -            profileFuture = null; -            lastuid = null; -            gfFuture = null; -            skinFuture=  null; -            fakePlayer= null; +            cancelRender();          }          if (uid != null && !uid.equals(lastuid) && (popupRect==null || !popupRect.contains(mouseX, mouseY))) { -            popupRect = null; -            profileFuture = null; -            gfFuture = null; -            skinFuture=  null; -            fakePlayer= null; +            cancelRender();              lastuid = uid;          }          if (lastuid == null) return; @@ -124,24 +126,42 @@ public class FeatureViewPlayerOnJoin extends SimpleFeature implements GuiPostRen          if (gfFuture == null) {              gfFuture = ApiFetchur.getSkinGameProfileByUUIDAsync(lastuid);          } +        boolean plsSetAPIKEY = false;          if (skinFuture == null && gfFuture.isDone()) { -            skinFuture = SkinFetchur.getSkinSet(gfFuture.get().orElse(null)); +            try { +                skinFuture = SkinFetchur.getSkinSet(gfFuture.get().orElse(null)); +            } catch (InterruptedException | ExecutionException e) { +                e.printStackTrace(); +            }          } -        if (fakePlayer == null && skinFuture != null && profileFuture != null && skinFuture.isDone() && profileFuture.isDone()) { -            fakePlayer = new FakePlayer(gfFuture.get().orElse(null), skinFuture.get(), profileFuture.get().orElse(null)); +        try { +            if (fakePlayer == null && skinFuture != null && profileFuture != null && skinFuture.isDone() && profileFuture.isDone() && profileFuture.get().isPresent()) { +                fakePlayer = new FakePlayer(gfFuture.get().orElse(null), skinFuture.get(), profileFuture.get().orElse(null)); +            } +        } catch (InterruptedException | ExecutionException e) { +            plsSetAPIKEY = true;          } -        render(popupRect, scaledResolution, mouseX, mouseY, profileFuture.isDone() ? profileFuture.get() : null); +        try { +            render(popupRect, scaledResolution, mouseX, mouseY, plsSetAPIKEY ? null : (profileFuture.isDone() ? profileFuture.get() : null), plsSetAPIKEY); +        } catch (InterruptedException | ExecutionException e) { +        } +      } -    public void render(Rectangle popupRect, ScaledResolution scaledResolution, int mouseX, int mouseY, Optional<PlayerProfile> playerProfile) { +    private void render(Rectangle popupRect, ScaledResolution scaledResolution, int mouseX, int mouseY, Optional<PlayerProfile> playerProfile, boolean apiKeyPlsSet) {          GlStateManager.pushMatrix();          GlStateManager.translate(popupRect.x, popupRect.y, 0);          Gui.drawRect(0,0, popupRect.width, popupRect.height, 0xFF23272a);          Gui.drawRect(2,2, popupRect.width-2, popupRect.height-2, 0XFF2c2f33); +        if (apiKeyPlsSet) { +            Minecraft.getMinecraft().fontRendererObj.drawString("Please set API KEY on /dg -> Party Kicker", 5,5, 0xFFFFFFFF); +            GlStateManager.popMatrix(); +            return; +        }          if (playerProfile == null) {              Minecraft.getMinecraft().fontRendererObj.drawString("Fetching data...", 5,5, 0xFFFFFFFF);              GlStateManager.popMatrix(); @@ -271,7 +291,6 @@ public class FeatureViewPlayerOnJoin extends SimpleFeature implements GuiPostRen              }          } catch (InterruptedException | ExecutionException e) { -            e.printStackTrace();          } @@ -297,6 +316,30 @@ public class FeatureViewPlayerOnJoin extends SimpleFeature implements GuiPostRen      @Override      public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) { +        String str = clientChatReceivedEvent.message.getFormattedText(); +        if (str.contains("§r§ejoined the dungeon group! (§r§b")) { +            String username = TextUtils.stripColor(str).split(" ")[3]; +            if (username.equalsIgnoreCase(Minecraft.getMinecraft().getSession().getUsername())) { +                Minecraft.getMinecraft().thePlayer.sendChatMessage("/pl"); +                PartyManager.INSTANCE.getRunOnMembersReceived().add((e) -> { +                    for (String s : e) { +                        ApiFetchur.fetchUUIDAsync(s) +                                .thenAccept(a -> { +                                    if (a == null) return; +                                    ApiFetchur.fetchMostRecentProfileAsync(a.get(), FeatureRegistry.PARTYKICKER_APIKEY.getAPIKey()); +                                    Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e"+s+"§f's Profile ").appendSibling(new ChatComponentText("§7view").setChatStyle(new ChatStyle().setChatHoverEvent(new FeatureViewPlayerOnJoin.HoverEventRenderPlayer(a.orElse(null)))))); +                                }); +                    } +                }); +            } else { +                ApiFetchur.fetchUUIDAsync(username) +                        .thenAccept(a -> { +                            if (a == null) return; +                            ApiFetchur.fetchMostRecentProfileAsync(a.get(), FeatureRegistry.PARTYKICKER_APIKEY.getAPIKey()); +                            Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e"+username+"§f's Profile ").appendSibling(new ChatComponentText("§7view").setChatStyle(new ChatStyle().setChatHoverEvent(new FeatureViewPlayerOnJoin.HoverEventRenderPlayer(a.orElse(null)))))); +                        }); +            } +        }      } diff --git a/src/main/java/kr/syeyoung/dungeonsguide/party/PartyManager.java b/src/main/java/kr/syeyoung/dungeonsguide/party/PartyManager.java index 2ba47dfc..8f938ca3 100644 --- a/src/main/java/kr/syeyoung/dungeonsguide/party/PartyManager.java +++ b/src/main/java/kr/syeyoung/dungeonsguide/party/PartyManager.java @@ -23,10 +23,8 @@ import org.json.JSONArray;  import org.json.JSONObject;  import java.security.SecureRandom; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.function.Consumer;  public class PartyManager implements StompMessageHandler {      public static final PartyManager INSTANCE = new PartyManager(); @@ -46,6 +44,10 @@ public class PartyManager implements StompMessageHandler {      private int invitedDash  =0;      @Getter +    private Queue<Consumer<Set<String>>> runOnMembersReceived = new LinkedList<>(); + + +    @Getter      @Setter      private int maxParty = 5; @@ -141,6 +143,10 @@ public class PartyManager implements StompMessageHandler {                  if (partyJoin == 2 || partyJoin == 100) {                      partyJoin = 0;                      // REQ PARTY JOIN +                    Consumer<Set<String>> r; +                    while ((r = runOnMembersReceived.poll()) != null){ +                        r.accept(members); +                    }                      JSONArray jsonArray = new JSONArray();                      for (String member : members) { | 
