aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/kr/syeyoung/dungeonsguide/features
diff options
context:
space:
mode:
authorsyeyoung <cyong06@naver.com>2021-04-24 14:54:52 +0900
committersyeyoung <cyong06@naver.com>2021-04-24 14:54:52 +0900
commite146d7f64df7782bebdb238b52bd67ed916abd6b (patch)
tree3b00824dba2e2f9f7f2527cd2407d230267ca975 /src/main/java/kr/syeyoung/dungeonsguide/features
parent5a2e091587c613c3bcd7acc58d7b464149c51bdd (diff)
downloadSkyblock-Dungeons-Guide-e146d7f64df7782bebdb238b52bd67ed916abd6b.tar.gz
Skyblock-Dungeons-Guide-e146d7f64df7782bebdb238b52bd67ed916abd6b.tar.bz2
Skyblock-Dungeons-Guide-e146d7f64df7782bebdb238b52bd67ed916abd6b.zip
commit #1
- YAY PARTY KCIKER!!@!@!
Diffstat (limited to 'src/main/java/kr/syeyoung/dungeonsguide/features')
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java5
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/APIKey.java25
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureViewPlayerOnJoin.java163
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ApiFetchur.java320
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ClassSpecificData.java11
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonClass.java30
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonSpecificData.java11
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonStat.java36
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonType.java21
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/FloorSpecificData.java11
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Pet.java13
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/PlayerProfile.java53
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Skill.java14
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/listener/ChatListenerGlobal.java7
14 files changed, 720 insertions, 0 deletions
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java
index f2046130..ac8610e9 100644
--- a/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java
@@ -10,6 +10,8 @@ import kr.syeyoung.dungeonsguide.features.impl.boss.terminal.FeatureTerminalSolv
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.FeatureViewPlayerOnJoin;
import kr.syeyoung.dungeonsguide.features.impl.secret.FeatureActions;
import kr.syeyoung.dungeonsguide.features.impl.secret.FeatureFreezePathfind;
import kr.syeyoung.dungeonsguide.features.impl.secret.FeatureMechanicBrowse;
@@ -82,6 +84,9 @@ public class FeatureRegistry {
public static final FeaturePenguins ETC_PENGUIN = register(new FeaturePenguins());
+ public static final APIKey PARTYKICKER_APIKEY = register(new APIKey());
+ public static final FeatureViewPlayerOnJoin PARTYKICKER_VIEWPLAYER = register(new FeatureViewPlayerOnJoin());
+
public static final FeatureWarningOnPortal BOSSFIGHT_WARNING_ON_PORTAL = register(new FeatureWarningOnPortal());
public static final SimpleFeature BOSSFIGHT_CHESTPRICE = register(new FeatureChestPrice());
public static final FeatureAutoReparty BOSSFIGHT_AUTOREPARTY = register(new FeatureAutoReparty());
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
new file mode 100644
index 00000000..ed708ec5
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/APIKey.java
@@ -0,0 +1,25 @@
+package kr.syeyoung.dungeonsguide.features.impl.party;
+
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.SimpleFeature;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListenerGlobal;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+
+public class APIKey extends SimpleFeature implements ChatListenerGlobal {
+
+ public APIKey() {
+ super("Party Kicker", "API KEY", "Set api key. Disabling this feature does nothing","partykicker.apikey");
+ parameters.put("apikey", new FeatureParameter<String>("apikey", "API Key", "API key", "","string"));
+ }
+
+ public String getAPIKey() {
+ return this.<String>getParameter("apikey").getValue();
+ }
+
+
+ @Override
+ public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) {
+ // ay set apikey
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureViewPlayerOnJoin.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureViewPlayerOnJoin.java
new file mode 100644
index 00000000..f7e2f020
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/FeatureViewPlayerOnJoin.java
@@ -0,0 +1,163 @@
+package kr.syeyoung.dungeonsguide.features.impl.party;
+
+import io.github.moulberry.hychat.HyChat;
+import io.github.moulberry.hychat.chat.ChatManager;
+import io.github.moulberry.hychat.gui.GuiChatBox;
+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.PlayerProfile;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.GuiPostRenderListener;
+import kr.syeyoung.dungeonsguide.gui.MPanel;
+import lombok.Getter;
+import lombok.SneakyThrows;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.*;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.event.HoverEvent;
+import net.minecraft.util.ChatComponentStyle;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.ChatStyle;
+import net.minecraft.util.IChatComponent;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.client.event.GuiScreenEvent;
+import net.minecraftforge.fml.common.Loader;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+public class FeatureViewPlayerOnJoin extends SimpleFeature implements GuiPostRenderListener, ChatListener {
+
+ public FeatureViewPlayerOnJoin() {
+ super("Party Kicker", "View player stats when join", "view player rendering when joining/someone joins the party", "partykicker.viewstats", true);
+ }
+
+ private Rectangle popupRect;
+ private String lastuid; // actually current uid
+ private Future<Optional<PlayerProfile>> profileFuture;
+ @SneakyThrows
+ @Override
+ public void onGuiPostRender(GuiScreenEvent.DrawScreenEvent.Post rendered) {
+ if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChat)) {
+ popupRect = null;
+ profileFuture = null;
+ 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();
+ }
+
+ if (!((popupRect != null && popupRect.contains(mouseX, mouseY)) || uid != null && uid.equals(lastuid))) {
+ popupRect = null;
+ profileFuture = null;
+ lastuid = null;
+ }
+
+ if (uid != null && !uid.equals(lastuid) && (popupRect==null || !popupRect.contains(mouseX, mouseY))) {
+ popupRect = null;
+ profileFuture = null;
+ lastuid = uid;
+ }
+ if (lastuid == null) return;
+
+
+ if (popupRect == null) {
+ popupRect = new Rectangle(mouseX, mouseY, 100, 200);
+ if (popupRect.y + popupRect.height > scaledResolution.getScaledHeight()) {
+ popupRect.y -= popupRect.y + popupRect.height - scaledResolution.getScaledHeight();
+ }
+ }
+
+ if (profileFuture == null) {
+ profileFuture = ApiFetchur.fetchMostRecentProfileAsync(lastuid, FeatureRegistry.PARTYKICKER_APIKEY.getAPIKey());
+ }
+
+ MPanel.clip(scaledResolution, popupRect.x, popupRect.y, popupRect.width, popupRect.height);
+
+ GL11.glEnable(GL11.GL_SCISSOR_TEST);
+ GlStateManager.pushMatrix();
+ GlStateManager.translate(popupRect.x, popupRect.y, 0);
+ Gui.drawRect(0,0, popupRect.width, popupRect.height, 0xFF000000);
+ System.out.println(lastuid + " - "+uid);
+ if (!profileFuture.isDone()) {
+ Minecraft.getMinecraft().fontRendererObj.drawString("Fetching data...", 5,5, 0xFFFFFFFF);
+ } else {
+ Optional<PlayerProfile> playerProfile = profileFuture.get();
+ if (playerProfile.isPresent()) {
+ Minecraft.getMinecraft().fontRendererObj.drawString(playerProfile.get().getMemberUID(), 5,5, 0xFFFFFFFF);
+ } else {
+ Minecraft.getMinecraft().fontRendererObj.drawString("User could not be found", 5,5, 0xFFFFFFFF);
+ }
+ }
+ GlStateManager.popMatrix();
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ }
+
+ public IChatComponent getHoveredComponent(ScaledResolution scaledResolution) {
+ IChatComponent ichatcomponent = null;
+ if (Loader.isModLoaded("hychat")) {
+ try {
+ ChatManager chatManager = HyChat.getInstance().getChatManager();
+ GuiChatBox guiChatBox = chatManager.getFocusedChat();
+
+ int x = guiChatBox.getX(scaledResolution);
+ int y = guiChatBox.getY(scaledResolution);
+ ichatcomponent = guiChatBox.chatArray.getHoveredComponent(guiChatBox.getSelectedTab().getChatLines(), Mouse.getX(), Mouse.getY(), x, y);
+ } catch (Throwable t) {}
+ }
+ if (ichatcomponent == null) {
+ ichatcomponent = Minecraft.getMinecraft().ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY());
+ }
+ return ichatcomponent;
+ }
+
+ @Override
+ public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) {
+ }
+
+ public static class HoverEventRenderPlayer extends HoverEvent {
+ @Getter
+ private String uuid;
+ public HoverEventRenderPlayer(String uuid) {
+ super(Action.SHOW_TEXT, new ChatComponentText(""));
+ this.uuid = uuid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ HoverEventRenderPlayer that = (HoverEventRenderPlayer) o;
+ return Objects.equals(uuid, that.uuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), uuid);
+ }
+
+ private IChatComponent cached;
+
+ @Override
+ public IChatComponent getValue() {
+ if (cached == null)
+ return cached = new ChatComponentText("").setChatStyle(new ChatStyle().setChatHoverEvent(new HoverEvent(Action.SHOW_TEXT, new ChatComponentText(uuid))));
+ return cached;
+ }
+ }
+}
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
new file mode 100644
index 00000000..00ede47d
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ApiFetchur.java
@@ -0,0 +1,320 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import com.google.gson.*;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompressedStreamTools;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import org.apache.commons.io.IOUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class ApiFetchur {
+ private static final Gson gson = new Gson();
+
+ private static final Map<String, CachedData<PlayerProfile>> playerProfileCache = new HashMap<>();
+ private static final Map<String, CachedData<String>> nicknameToUID = new HashMap<>();
+ private static final ExecutorService ex = Executors.newFixedThreadPool(4);
+
+ public static void purgeCache() {
+ playerProfileCache.clear();
+ nicknameToUID.clear();
+ }
+
+ @Data
+ @AllArgsConstructor
+ public static class CachedData<T> {
+ private final long expire;
+ private final T data;
+ }
+
+ public static JsonObject getJson(String url) throws IOException {
+ URLConnection connection = new URL(url).openConnection();
+ connection.setConnectTimeout(10000);
+ connection.setReadTimeout(10000);
+ return gson.fromJson(new InputStreamReader(connection.getInputStream()), JsonObject.class);
+ }
+
+ public static CompletableFuture<Optional<PlayerProfile>> fetchMostRecentProfileAsync(String uid, String apiKey) {
+ if (playerProfileCache.containsKey(uid)) {
+ CachedData<PlayerProfile> cachedData = playerProfileCache.get(uid);
+ if (cachedData.expire > System.currentTimeMillis()) {
+ return CompletableFuture.completedFuture(Optional.ofNullable(cachedData.data));
+ }
+ playerProfileCache.remove(uid);
+ }
+
+ CompletableFuture<Optional<PlayerProfile>> completableFuture = new CompletableFuture<>();
+ ex.submit(() -> {
+ try {
+ Optional<PlayerProfile> playerProfile = fetchMostRecentProfile(uid, apiKey);
+ playerProfileCache.put(uid, new CachedData<PlayerProfile>(System.currentTimeMillis()+1000*60*30, playerProfile.orElse(null)));
+ completableFuture.complete(playerProfile);
+ return;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ playerProfileCache.put(uid, new CachedData<PlayerProfile>(System.currentTimeMillis()+1000*60*30, null));
+ completableFuture.complete(Optional.empty());
+ });
+ return completableFuture;
+ }
+
+ public static CompletableFuture<Optional<String>> fetchUUIDAsync(String nickname) {
+ if (nicknameToUID.containsKey(nickname)) {
+ CachedData<String> cachedData = nicknameToUID.get(nickname);
+ if (cachedData.expire > System.currentTimeMillis()) {
+ return CompletableFuture.completedFuture(Optional.ofNullable(cachedData.data));
+ }
+ nicknameToUID.remove(nickname);
+ }
+
+
+ CompletableFuture<Optional<String>> completableFuture = new CompletableFuture<>();
+
+ ex.submit(() -> {
+ try {
+ Optional<String> playerProfile = fetchUUID(nickname);
+ nicknameToUID.put(nickname, new CachedData<String>(System.currentTimeMillis()+1000*60*60,playerProfile.orElse(null)));
+ completableFuture.complete(playerProfile);
+ return;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ nicknameToUID.put(nickname, new CachedData<String>(System.currentTimeMillis()+1000*60*30, null));
+ completableFuture.complete(Optional.empty());
+ });
+
+ return completableFuture;
+ }
+
+ public static Optional<String> fetchUUID(String nickname) throws IOException {
+ JsonObject json = getJson("https://api.mojang.com/users/profiles/minecraft/"+nickname);
+ if (json.has("error")) return Optional.empty();
+ return Optional.of(TextUtils.insertDashUUID(json.get("id").getAsString()));
+ }
+
+ public static List<PlayerProfile> fetchPlayerProfiles(String uid, String apiKey) throws IOException {
+ JsonObject json = getJson("https://api.hypixel.net/skyblock/profiles?uuid="+uid+"&key="+apiKey);
+ if (!json.get("success").getAsBoolean()) return new ArrayList<>();
+ JsonArray profiles = json.getAsJsonArray("profiles");
+ String dashTrimmed = uid.replace("-", "");
+
+ ArrayList<PlayerProfile> playerProfiles = new ArrayList<>();
+ for (JsonElement jsonElement : profiles) {
+ JsonObject semiProfile = jsonElement.getAsJsonObject();
+ if (!semiProfile.has(dashTrimmed)) continue;
+ playerProfiles.add(parseProfile(semiProfile, dashTrimmed));
+ }
+ return playerProfiles;
+ }
+
+ public static Optional<PlayerProfile> fetchMostRecentProfile(String uid, String apiKey) throws IOException {
+ JsonObject json = getJson("https://api.hypixel.net/skyblock/profiles?uuid="+uid+"&key="+apiKey);
+ if (!json.get("success").getAsBoolean()) return Optional.empty();
+ JsonArray profiles = json.getAsJsonArray("profiles");
+ String dashTrimmed = uid.replace("-", "");
+
+ JsonObject profile = null;
+ long lastSave = Long.MIN_VALUE;
+ for (JsonElement jsonElement : profiles) {
+ JsonObject semiProfile = jsonElement.getAsJsonObject();
+ if (!semiProfile.getAsJsonObject("members").has(dashTrimmed)) continue;
+ long lastSave2 = semiProfile.getAsJsonObject("members").getAsJsonObject(dashTrimmed).get("last_save").getAsLong();
+ if (lastSave2 > lastSave) {
+ profile = semiProfile;
+ lastSave = lastSave2;
+ }
+ }
+
+ if (profile == null) return Optional.empty();
+
+ return Optional.of(parseProfile(profile, dashTrimmed));
+ }
+
+ public static int getOrDefault(JsonObject jsonObject, String key, int value) {
+ if (jsonObject == null || !jsonObject.has(key) || jsonObject.get(key) instanceof JsonNull) return value;
+ return jsonObject.get(key).getAsInt();
+ }
+ public static long getOrDefault(JsonObject jsonObject, String key, long value) {
+ if (jsonObject == null || !jsonObject.has(key) || jsonObject.get(key) instanceof JsonNull) return value;
+ return jsonObject.get(key).getAsLong();
+ }
+ public static double getOrDefault(JsonObject jsonObject, String key, double value) {
+ if (jsonObject == null || !jsonObject.has(key) || jsonObject.get(key) instanceof JsonNull) return value;
+ return jsonObject.get(key).getAsDouble();
+ }
+ public static String getOrDefault(JsonObject jsonObject, String key, String value) {
+ if (jsonObject == null || !jsonObject.has(key) || jsonObject.get(key) instanceof JsonNull) return value;
+ return jsonObject.get(key).getAsString();
+ }
+ public static NBTTagCompound parseBase64NBT(String nbt) throws IOException {
+ return CompressedStreamTools.readCompressed(new ByteArrayInputStream(Base64.getDecoder().decode(nbt)));
+ }
+
+ public static ItemStack deserializeNBT(NBTTagCompound nbtTagCompound) {
+ if (nbtTagCompound.hasNoTags()) return null;
+ ItemStack itemStack = new ItemStack(Blocks.stone);
+ itemStack.deserializeNBT(nbtTagCompound);
+ return itemStack;
+ }
+
+ public static PlayerProfile parseProfile(JsonObject profile, String dashTrimmed) throws IOException {
+ PlayerProfile playerProfile = new PlayerProfile();
+ playerProfile.setProfileUID(getOrDefault(profile, "profile_id", ""));
+ playerProfile.setMemberUID(dashTrimmed);
+ playerProfile.setProfileName(getOrDefault(profile, "cute_name", ""));
+
+ JsonObject playerData = profile.getAsJsonObject("members").getAsJsonObject(dashTrimmed);
+ playerProfile.setLastSave(getOrDefault(playerData, "last_save", 0L));
+ playerProfile.setFairySouls(getOrDefault(playerData, "fairy_souls_collected", 0));
+ playerProfile.setFairyExchanges(getOrDefault(playerData, "fairy_exchanges", 0));
+
+ if (playerData.has("inv_armor")) {
+ playerProfile.setCurrentArmor(new PlayerProfile.Armor());
+ NBTTagCompound armor = parseBase64NBT(playerData.getAsJsonObject("inv_armor")
+ .get("data")
+ .getAsString());
+ NBTTagList array = armor.getTagList("i", 10);
+ for (int i = 0; i < 4; i++) {
+ NBTTagCompound item = array.getCompoundTagAt(i);
+ playerProfile.getCurrentArmor().getArmorSlots()[i] = deserializeNBT(item);
+ }
+ }
+
+ if (playerData.has("wardrobe_contents")) {
+ NBTTagCompound armor = parseBase64NBT(playerData.getAsJsonObject("wardrobe_contents").get("data").getAsString());
+ NBTTagList array = armor.getTagList("i", 10);
+ for (int i = 0; i < array.tagCount(); i++) {
+ if (i % 4 == 0) playerProfile.getWardrobe().add(new PlayerProfile.Armor());
+ NBTTagCompound item = array.getCompoundTagAt(i);
+ playerProfile.getWardrobe().get(i/4).getArmorSlots()[i%4] = deserializeNBT(item);
+ }
+
+ }
+ playerProfile.setSelectedWardrobe(getOrDefault(playerData, "wardrobe_equipped_slot", -1));
+
+ if (playerData.has("inv_contents")) {
+ NBTTagCompound armor = parseBase64NBT(playerData.getAsJsonObject("inv_contents").get("data").getAsString());
+ NBTTagList array = armor.getTagList("i", 10);
+ playerProfile.setInventory(new ItemStack[array.tagCount()]);
+ for (int i = 0; i < array.tagCount(); i++) {
+ NBTTagCompound item = array.getCompoundTagAt(i);
+ playerProfile.getInventory()[i] = deserializeNBT(item);
+ }
+ }
+ if (playerData.has("ender_chest_contents")) {
+ NBTTagCompound armor = parseBase64NBT(playerData.getAsJsonObject("ender_chest_contents").get("data").getAsString());
+ NBTTagList array = armor.getTagList("i", 10);
+ playerProfile.setEnderchest(new ItemStack[array.tagCount()]);
+ for (int i = 0; i < array.tagCount(); i++) {
+ NBTTagCompound item = array.getCompoundTagAt(i);
+ playerProfile.getEnderchest()[i] = deserializeNBT(item);
+ }
+ }
+ if (playerData.has("talisman_bag")) {
+ NBTTagCompound armor = parseBase64NBT(playerData.getAsJsonObject("talisman_bag").get("data").getAsString());
+ NBTTagList array = armor.getTagList("i", 10);
+ playerProfile.setTalismans(new ItemStack[array.tagCount()]);
+ for (int i = 0; i < array.tagCount(); i++) {
+ NBTTagCompound item = array.getCompoundTagAt(i);
+ playerProfile.getTalismans()[i] = deserializeNBT(item);
+ }
+ }
+
+ for (Skill value : Skill.values()) {
+ playerProfile.getSkillXp().put(value, getOrDefault(playerData, "experience_skill_"+value.getJsonName(), 0.0));
+ }
+
+ if (playerData.has("pets")) {
+ for (JsonElement pets : playerData.getAsJsonArray("pets")) {
+ JsonObject pet = pets.getAsJsonObject();
+ Pet petObj = new Pet();
+ petObj.setActive(pet.get("active").getAsBoolean());
+ petObj.setExp(getOrDefault(pet, "exp", 0.0));
+ petObj.setHeldItem(getOrDefault(pet, "heldItem", null));
+ petObj.setSkin(getOrDefault(pet, "skin", null));
+ petObj.setType(getOrDefault(pet, "type", null));
+ petObj.setUuid(getOrDefault(pet, "uuid", null));
+
+ playerProfile.getPets().add(petObj);
+ }
+ }
+
+ if (playerData.has("dungeons") && playerData.getAsJsonObject("dungeons").has("dungeon_types")) {
+ JsonObject types = playerData.getAsJsonObject("dungeons")
+ .getAsJsonObject("dungeon_types");
+ for (DungeonType value : DungeonType.values()) {
+ DungeonStat dungeonStat = new DungeonStat();
+ DungeonSpecificData<DungeonStat> dungeonSpecificData = new DungeonSpecificData<>(value, dungeonStat);
+ playerProfile.getDungeonStats().put(value, dungeonSpecificData);
+
+ if (!types.has(value.getJsonName())) continue;
+
+ JsonObject dungeonObj = types.getAsJsonObject(value.getJsonName());
+
+ dungeonStat.setHighestCompleted(getOrDefault(dungeonObj, "highest_tier_completed", -1));
+
+ for (Integer validFloor : value.getValidFloors()) {
+ DungeonStat.PlayedFloor playedFloor = new DungeonStat.PlayedFloor();
+ playedFloor.setBestScore(getOrDefault(dungeonObj.getAsJsonObject("best_score"), ""+validFloor, 0));
+ playedFloor.setCompletions(getOrDefault(dungeonObj.getAsJsonObject("tier_completions"), ""+validFloor, 0));
+ playedFloor.setFastestTime(getOrDefault(dungeonObj.getAsJsonObject("fastest_time"), ""+validFloor, -1));
+ playedFloor.setFastestTimeS(getOrDefault(dungeonObj.getAsJsonObject("fastest_time_s"), ""+validFloor, -1));
+ playedFloor.setFastestTimeSPlus(getOrDefault(dungeonObj.getAsJsonObject("fastest_time_s_plus"), ""+validFloor, -1));
+ playedFloor.setMobsKilled(getOrDefault(dungeonObj.getAsJsonObject("mobs_killed"), ""+validFloor, 0));
+ playedFloor.setMostMobsKilled(getOrDefault(dungeonObj.getAsJsonObject("most_mobs_killed"), ""+validFloor, 0));
+ playedFloor.setMostHealing(getOrDefault(dungeonObj.getAsJsonObject("most_healing"), ""+validFloor, 0));
+ playedFloor.setTimes_played(getOrDefault(dungeonObj.getAsJsonObject("times_played"), ""+validFloor, 0));
+ playedFloor.setWatcherKills(getOrDefault(dungeonObj.getAsJsonObject("watcher_kills"), ""+validFloor, 0));
+
+ for (DungeonClass dungeonClass : DungeonClass.values()) {
+ DungeonStat.PlayedFloor.ClassStatistics classStatistics = new DungeonStat.PlayedFloor.ClassStatistics();
+ classStatistics.setMostDamage(getOrDefault(dungeonObj.getAsJsonObject("most_damage_"+dungeonClass.getJsonName()), ""+validFloor, 0));
+ ClassSpecificData<DungeonStat.PlayedFloor.ClassStatistics> classStatisticsClassSpecificData = new ClassSpecificData<>(dungeonClass, classStatistics);
+
+ playedFloor.getClassStatistics().put(dungeonClass, classStatisticsClassSpecificData);
+ }
+
+ FloorSpecificData<DungeonStat.PlayedFloor> playedFloorFloorSpecificData = new FloorSpecificData<>(validFloor, playedFloor);
+ dungeonStat.getPlays().put(validFloor, playedFloorFloorSpecificData);
+ }
+
+ dungeonStat.setExperience(getOrDefault(dungeonObj, "experience", 0));
+
+
+ }
+ }
+ if (playerData.has("dungeons") && playerData.getAsJsonObject("dungeons").has("player_classes")) {
+ JsonObject classes = playerData.getAsJsonObject("dungeons")
+ .getAsJsonObject("player_classes");
+ for (DungeonClass dungeonClass : DungeonClass.values()) {
+ PlayerProfile.PlayerClassData classStatistics = new PlayerProfile.PlayerClassData();
+ classStatistics.setExperience(getOrDefault(classes.getAsJsonObject(dungeonClass.getJsonName()), "experience", 0));
+ ClassSpecificData<PlayerProfile.PlayerClassData> classStatisticsClassSpecificData = new ClassSpecificData<>(dungeonClass, classStatistics);
+
+ playerProfile.getPlayerClassData().put(dungeonClass, classStatisticsClassSpecificData);
+ }
+ }
+ if (playerData.has("dungeons")) {
+ String id = getOrDefault(playerData, "selected_dungeon_class", null);
+ DungeonClass dungeonClass = DungeonClass.getClassByJsonName(id);
+ playerProfile.setSelectedClass(dungeonClass);
+ }
+
+ return playerProfile;
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ClassSpecificData.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ClassSpecificData.java
new file mode 100644
index 00000000..e7b7abc4
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/ClassSpecificData.java
@@ -0,0 +1,11 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class ClassSpecificData<T> {
+ private DungeonClass dungeonClass;
+ private T data;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonClass.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonClass.java
new file mode 100644
index 00000000..a1d63f05
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonClass.java
@@ -0,0 +1,30 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Getter
+public enum DungeonClass {
+ MAGE("mage"), ARCHER("archer"), HEALER("healer"), TANK("tank"), BERSERK("berserk");
+
+
+ private String jsonName;
+ private DungeonClass(String jsonName) {
+ this.jsonName = jsonName;
+ }
+
+ private static final Map<String, DungeonClass> jsonNameToClazz = new HashMap<>();
+ static {
+ for (DungeonClass value : values()) {
+ jsonNameToClazz.put(value.getJsonName(), value);
+ }
+ }
+
+ public static DungeonClass getClassByJsonName(String name) {
+ return jsonNameToClazz.get(name);
+ }
+
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonSpecificData.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonSpecificData.java
new file mode 100644
index 00000000..63187676
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonSpecificData.java
@@ -0,0 +1,11 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class DungeonSpecificData<T> {
+ private final DungeonType type;
+ private final T data;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonStat.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonStat.java
new file mode 100644
index 00000000..c8702e72
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonStat.java
@@ -0,0 +1,36 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class DungeonStat {
+ private int highestCompleted;
+ private double experience;
+
+ private Map<Integer, FloorSpecificData<PlayedFloor>> plays = new HashMap<>();
+ @Data
+ public static class PlayedFloor {
+ private int times_played;
+ private int completions;
+ private int watcherKills;
+
+ private int fastestTime;
+ private int fastestTimeS;
+ private int fastestTimeSPlus;
+ private int bestScore;
+
+ private int mostMobsKilled;
+ private int mobsKilled;
+
+ private Map<DungeonClass, ClassSpecificData<ClassStatistics>> classStatistics = new HashMap<>();
+ @Data
+ public static class ClassStatistics {
+ private double mostDamage;
+ }
+
+ private double mostHealing;
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonType.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonType.java
new file mode 100644
index 00000000..bf86e97e
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/DungeonType.java
@@ -0,0 +1,21 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import com.google.common.collect.Sets;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Set;
+
+@Getter
+@AllArgsConstructor
+public enum DungeonType {
+ CATACOMBS("catacombs", "The Catacombs",
+ Sets.newHashSet(0,1,2,3,4,5,6,7)),
+ MASTER_CATACOMBS("master_catacombs", "MasterMode Catacombs", Sets.newHashSet(
+ 1,2,3,4,5,6
+ ));
+
+ private final String jsonName;
+ private final String familiarName;
+ private final Set<Integer> validFloors ;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/FloorSpecificData.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/FloorSpecificData.java
new file mode 100644
index 00000000..54824406
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/FloorSpecificData.java
@@ -0,0 +1,11 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class FloorSpecificData<T> {
+ private final int floor;
+ private final T data;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Pet.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Pet.java
new file mode 100644
index 00000000..3ffac6ba
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Pet.java
@@ -0,0 +1,13 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.Data;
+
+@Data
+public class Pet {
+ private String uuid;
+ private String type;
+ private double exp;
+ private boolean active;
+ private String heldItem;
+ private String skin;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/PlayerProfile.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/PlayerProfile.java
new file mode 100644
index 00000000..7cc38a3e
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/PlayerProfile.java
@@ -0,0 +1,53 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.Data;
+import net.minecraft.item.ItemStack;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class PlayerProfile {
+ private String profileUID;
+ private String memberUID;
+ private String profileName;
+
+ private long lastSave;
+
+ private int fairySouls;
+ private int fairyExchanges;
+
+ private Armor currentArmor;
+ private List<Armor> wardrobe = new ArrayList<>();
+ private int selectedWardrobe = -1;
+
+ private ItemStack[] inventory;
+ private ItemStack[] enderchest;
+ private ItemStack[] talismans;
+
+ @Data
+ public static class Armor {
+ private final ItemStack[] armorSlots = new ItemStack[4];
+
+ public ItemStack getHelmet() { return armorSlots[3]; }
+ public ItemStack getChestPlate() { return armorSlots[2]; }
+ public ItemStack getLeggings() { return armorSlots[1]; }
+ public ItemStack getBoots() { return armorSlots[0]; }
+ }
+
+ private Map<DungeonType, DungeonSpecificData<DungeonStat>> dungeonStats = new HashMap<>();
+
+ private Map<DungeonClass, ClassSpecificData<PlayerClassData>> playerClassData = new HashMap<>();
+ private DungeonClass selectedClass;
+ @Data
+ public static class PlayerClassData {
+ private double experience;
+ }
+
+ private Map<Skill, Double> skillXp = new HashMap<>();
+
+ private List<Pet> pets = new ArrayList<>();
+
+} \ No newline at end of file
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Skill.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Skill.java
new file mode 100644
index 00000000..fd88144b
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/party/api/Skill.java
@@ -0,0 +1,14 @@
+package kr.syeyoung.dungeonsguide.features.impl.party.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum Skill {
+ RUNECRAFTING("runecrafting", "Runecrafting"), COMBAT("combat", "Combat"), MINING("mining", "Mining"), ALCHEMY("alchemy", "Alchemy"), FARMING("farming", "Farming"), TAMING("taming", "Taming"), ENCHANTING("enchanting", "Enchanting"), FISHING("fishing", "Fishing"), FORAGING("foraging", "Foraging"), CARPENTRY("carpentry", "Carpentry");
+
+ private final String jsonName;
+ private final String friendlyName;
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/listener/ChatListenerGlobal.java b/src/main/java/kr/syeyoung/dungeonsguide/features/listener/ChatListenerGlobal.java
new file mode 100644
index 00000000..423de9b2
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/listener/ChatListenerGlobal.java
@@ -0,0 +1,7 @@
+package kr.syeyoung.dungeonsguide.features.listener;
+
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+
+public interface ChatListenerGlobal {
+ void onChat(ClientChatReceivedEvent clientChatReceivedEvent);
+}