From 4d65b2d19322300703b1894b061c6e1a03a07f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Voln=C3=BD?= Date: Sun, 5 Apr 2020 23:14:12 +0200 Subject: [PATCH] Add custom string compression, move custom tips API to a new class, minify the json message format - the string compression algorithm tries to replace duplicate words (space delimited strings) with short tokens. It is used to compress the power tips section - also most of the prints were disabled, stuff should print only if something's wrong --- .../java/str_exporter/BackendBroadcaster.java | 21 +- src/main/java/str_exporter/CustomTipsAPI.java | 135 +++++++++++++ .../java/str_exporter/JSONMessageBuilder.java | 181 ++++++------------ .../str_exporter/SlayTheRelicsExporter.java | 85 +------- .../java/str_exporter/StringCompression.java | 137 +++++++++++++ 5 files changed, 351 insertions(+), 208 deletions(-) create mode 100644 src/main/java/str_exporter/CustomTipsAPI.java create mode 100644 src/main/java/str_exporter/StringCompression.java diff --git a/src/main/java/str_exporter/BackendBroadcaster.java b/src/main/java/str_exporter/BackendBroadcaster.java index 033ad57..8d76f44 100644 --- a/src/main/java/str_exporter/BackendBroadcaster.java +++ b/src/main/java/str_exporter/BackendBroadcaster.java @@ -9,20 +9,18 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.LinkedList; import java.util.concurrent.locks.ReentrantLock; public class BackendBroadcaster { public static final Logger logger = LogManager.getLogger(BackendBroadcaster.class.getName()); - private static final String EBS_URL = "https://localhost:8081"; -// private static final String EBS_URL = "https://slaytherelics.xyz:8081"; +// private static final String EBS_URL = "https://localhost:8081"; + private static final String EBS_URL = "https://slaytherelics.xyz:8081"; private static final long CHECK_QUEUE_PERIOD_MILLIS = 100; private static BackendBroadcaster instance = new BackendBroadcaster(); -// public static long encodingDelay = 0; private String message; private long messageTimestamp; private ReentrantLock queueLock; @@ -91,6 +89,13 @@ private static String injectDelayToMessage(String msg, long delay) { return "{\"d\":" + delay + "," + msg.substring(1); } + private static String compressPowerTips(String msg) { + int index = msg.lastIndexOf("\"w\":\""); + + String comp_powertips = StringCompression.compress(msg.substring(index + 5, msg.length() - 3)); + return msg.substring(0, index + 5) + comp_powertips + "\"}}"; + } + private void broadcastMessage(String msg, long msgTimestamp) { try { @@ -101,6 +106,7 @@ private void broadcastMessage(String msg, long msgTimestamp) { con.setRequestProperty("Accept", "application/json"); con.setDoOutput(true); + msg = compressPowerTips(msg); long msgDelay = msgTimestamp - System.currentTimeMillis() + SlayTheRelicsExporter.delay; msg = injectDelayToMessage(msg, msgDelay); @@ -115,11 +121,14 @@ private void broadcastMessage(String msg, long msgTimestamp) { response.append(responseLine.trim()); } - logger.info(msg); - logger.info("broadcasted message, response: " + response.toString()); + if (!response.toString().equals("Success")) + logger.info("message not broadcasted succesfully, response: " + response.toString()); +// logger.info("broadcasted message, response: " + response.toString()); } catch (Exception e) { e.printStackTrace(); + } finally { +// logger.info(SlayTheRelicsExporter.removeSecret(msg)); } } } diff --git a/src/main/java/str_exporter/CustomTipsAPI.java b/src/main/java/str_exporter/CustomTipsAPI.java new file mode 100644 index 0000000..566471e --- /dev/null +++ b/src/main/java/str_exporter/CustomTipsAPI.java @@ -0,0 +1,135 @@ +package str_exporter; + +import basemod.BaseMod; +import basemod.interfaces.PostInitializeSubscriber; +import com.evacipated.cardcrawl.modthespire.Loader; +import com.evacipated.cardcrawl.modthespire.ModInfo; +import com.evacipated.cardcrawl.modthespire.Patcher; +import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer; +import com.megacrit.cardcrawl.helpers.Hitbox; +import com.megacrit.cardcrawl.helpers.PowerTip; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Set; + +@SpireInitializer +public class CustomTipsAPI implements PostInitializeSubscriber { + + private static final String CUSTOM_TIP_HITBOX_NAME = "slayTheRelicsHitboxes"; + private static final String CUSTOM_TIP_POWERTIPS_NAME = "slayTheRelicsPowerTips"; + + private ArrayList externalHitboxFields = new ArrayList<>(); + private ArrayList externalPowerTipsFields = new ArrayList<>(); + + private static Logger logger = SlayTheRelicsExporter.logger; + + public static CustomTipsAPI instance; + + private CustomTipsAPI() { + BaseMod.subscribe(this); + } + + public static void initialize() { + instance = new CustomTipsAPI(); + } + + @Override + public void receivePostInitialize() { + findCustomTipImplementingClasses(); + } + + private void findCustomTipImplementingClasses() { + logger.info("CHECKING FOR OTHER MODS THAT IMPLEMENT CUSTOM TIPS API"); + + ClassLoader loader = SlayTheRelicsExporter.class.getClassLoader(); + + for (ModInfo info : Loader.MODINFOS) { + if (Patcher.annotationDBMap.containsKey(info.jarURL)) { + Set initializers = Patcher.annotationDBMap.get(info.jarURL).getAnnotationIndex().get(SpireInitializer.class.getName()); + if (initializers != null) { + System.out.println(" - " + info.Name); + for (String initializer : initializers) { + System.out.println(" - " + initializer); + try { + long startTime = System.nanoTime(); + + Class c = loader.loadClass(initializer); + + try { + Field hitboxes = c.getField(CUSTOM_TIP_HITBOX_NAME); + Field powerTips = c.getField(CUSTOM_TIP_POWERTIPS_NAME); + + if (hitboxes.getType() == ArrayList.class && powerTips.getType() == ArrayList.class) { + externalHitboxFields.add(hitboxes); + externalPowerTipsFields.add(powerTips); + logger.info("Fields found for class " + c.getCanonicalName()); + } + } catch (NoSuchFieldException e) { + logger.info("No fields found for class " + c.getCanonicalName()); + } + + long endTime = System.nanoTime(); + long duration = endTime - startTime; + logger.info(" - " + (duration / 1000000) + "ms"); + } catch (ClassNotFoundException e) { + logger.info("WARNING: Unable to find method initialize() on class marked @SpireInitializer: " + initializer); + } + } + } + } else { + System.err.println(info.jarURL + " Not in DB map. Something is very wrong"); + } + } + } + + public Object[] getTipsFromMods() { + + LinkedList hitboxes = new LinkedList<>(); + LinkedList> tip_lists = new LinkedList<>(); + + for (int i = 0; i < externalHitboxFields.size(); i++) { + + try { + +// logger.info("class: " + hitbox_fields.get(i).getDeclaringClass().getSimpleName()); + + ArrayList mod_hitboxes = (ArrayList) externalHitboxFields.get(i).get(null); + ArrayList> mod_tip_lists = (ArrayList>) externalPowerTipsFields.get(i).get(null); + + if (mod_hitboxes.size() == mod_tip_lists.size()) { +// logger.info("found fields, adding " + mod_hitboxes.size() + " entries"); + hitboxes.addAll(mod_hitboxes); + tip_lists.addAll(removeImagesFromPowerTipsLists(mod_tip_lists)); + } else { +// logger.info("hitboxes and powertip list don't have the same size for class: " + hitbox_fields.get(i).getDeclaringClass().getSimpleName()); + } + + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + return new Object[]{hitboxes, tip_lists}; + } + + + + private static ArrayList> removeImagesFromPowerTipsLists(ArrayList> tip_lists) { + // removes any images + ArrayList> new_tip_lists = new ArrayList<>(tip_lists.size()); + + for (int i = 0; i < tip_lists.size(); i++) { + ArrayList list = tip_lists.get(i); + + new_tip_lists.add(new ArrayList<>(list.size())); + for (int j = 0; j < list.size(); j++) { + new_tip_lists.get(i).add(new PowerTip(list.get(j).header, list.get(j).body)); + } + } + + return new_tip_lists; + } +} diff --git a/src/main/java/str_exporter/JSONMessageBuilder.java b/src/main/java/str_exporter/JSONMessageBuilder.java index bc65457..525983c 100644 --- a/src/main/java/str_exporter/JSONMessageBuilder.java +++ b/src/main/java/str_exporter/JSONMessageBuilder.java @@ -3,8 +3,6 @@ import basemod.ReflectionHacks; import basemod.patches.whatmod.WhatMod; import com.badlogic.gdx.graphics.Texture; -import com.evacipated.cardcrawl.modthespire.Loader; -import com.evacipated.cardcrawl.modthespire.ModInfo; import com.megacrit.cardcrawl.characters.AbstractPlayer; import com.megacrit.cardcrawl.core.CardCrawlGame; import com.megacrit.cardcrawl.core.Settings; @@ -19,13 +17,14 @@ import com.megacrit.cardcrawl.powers.AbstractPower; import com.megacrit.cardcrawl.relics.AbstractRelic; import com.megacrit.cardcrawl.rooms.AbstractRoom; -import com.megacrit.cardcrawl.ui.buttons.PeekButton; import com.megacrit.cardcrawl.ui.panels.TopPanel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; import java.util.Locale; public class JSONMessageBuilder { @@ -44,7 +43,7 @@ public JSONMessageBuilder(String login, String secret, String version) { public String buildJson() { - powerTips = new ArrayList<>(); + powerTips = new ArrayList<>(40); String character = ""; @@ -54,36 +53,36 @@ public String buildJson() { StringBuilder sb = new StringBuilder(); sb.append("{"); - sb.append("\"msg_type\": \"set_content\", "); + sb.append("\"msg_type\":1,"); sb.append("\"streamer\": {\"login\": \"" + login + "\", \"secret\": \"" + secret + "\"}, "); sb.append("\"meta\": {\"version\": \"" + version + "\"}, "); sb.append("\"message\": {"); - sb.append("\"character\": \"" + character + "\", "); + sb.append("\"c\":\"" + character + "\","); // character - sb.append("\"relics\": "); + sb.append("\"r\":"); // relics buildRelics(sb); - sb.append(", "); + sb.append(","); - sb.append("\"potions\": "); + sb.append("\"o\":"); buildPotions(sb); - sb.append(", "); + sb.append(","); if (isInCombat()) { // && (!AbstractDungeon.isScreenUp || PeekButton.isPeeking) - sb.append("\"player_powers\": "); + sb.append("\"p\":"); buildPlayerPowers(sb); - sb.append(", "); + sb.append(","); - sb.append("\"monster_powers\": "); + sb.append("\"m\":"); buildMonsterPowers(sb); - sb.append(", "); + sb.append(","); - sb.append("\"custom_tips\": "); + sb.append("\"u\":"); buildCustomTips(sb); - sb.append(", "); + sb.append(","); } - sb.append("\"power_tips\": "); + sb.append("\"w\":"); buildPowerTips(sb); sb.append("}}"); @@ -99,15 +98,14 @@ private void buildPlayerPowers(StringBuilder sb) { float y = (Settings.HEIGHT - player.hb.y - player.hb.height) / Settings.HEIGHT* 100f; // invert the y-coordinate float w = player.hb.width / Settings.WIDTH * 100f; float h = (player.hb.height + player.healthHb.height) / Settings.HEIGHT * 100f; - sb.append(String.format(Locale.US, "{\"hitbox\": {\"x\": %.3f, \"y\": %.3f, \"w\": %.3f, \"h\": %.3f}, ", x, y, w, h)); + sb.append(String.format(Locale.US, "[%.2f,%.2f,%.2f,%.2f,", x, y, w, h)); - sb.append("\"power_tips\": "); ArrayList tipsPrefix = new ArrayList<>(); if (!player.stance.ID.equals("Neutral")) { tipsPrefix.add(new PowerTip(player.stance.name, player.stance.description)); } buildPowers(sb, player.powers, tipsPrefix); - sb.append('}'); + sb.append(']'); } private void buildMonsterPowers(StringBuilder sb) { @@ -121,19 +119,18 @@ private void buildMonsterPowers(StringBuilder sb) { float y = (Settings.HEIGHT - monster.hb.y - monster.hb.height) / Settings.HEIGHT* 100f; // invert the y-coordinate float w = monster.hb.width / Settings.WIDTH * 100f; float h = (monster.hb.height + monster.healthHb.height) / Settings.HEIGHT * 100f; - sb.append(String.format(Locale.US, "{\"hitbox\": {\"x\": %.3f, \"y\": %.3f, \"w\": %.3f, \"h\": %.3f}, ", x, y, w, h)); + sb.append(String.format(Locale.US, "[%.2f,%.2f,%.2f,%.2f,", x, y, w, h)); - sb.append("\"power_tips\": "); ArrayList tipsPrefix = new ArrayList<>(); if (monster.intentAlphaTarget == 1.0F && !AbstractDungeon.player.hasRelic("Runic Dome") && monster.intent != AbstractMonster.Intent.NONE) { PowerTip intentTip = (PowerTip) ReflectionHacks.getPrivate(monster, AbstractMonster.class, "intentTip"); tipsPrefix.add(intentTip); } buildPowers(sb, monster.powers, tipsPrefix); - sb.append('}'); + sb.append(']'); if (i < monsters.size() - 1) - sb.append(", "); + sb.append(","); } sb.append(']'); } @@ -148,33 +145,37 @@ private void buildCustomTips(StringBuilder sb) { StringBuilder sb_safe = new StringBuilder(); String result = ""; try { - Object[] res = getTipsFromMods(); - ArrayList hitboxes = (ArrayList) res[0]; - ArrayList> tip_lists = (ArrayList>) res[1]; + long start = System.currentTimeMillis(); + Object[] res = CustomTipsAPI.instance.getTipsFromMods(); + + LinkedList hitboxes = (LinkedList) res[0]; + LinkedList> tip_lists = (LinkedList>) res[1]; + long end = System.currentTimeMillis(); - logger.info("hitboxes size: " + hitboxes.size()); +// logger.info("getTipsFromMods() took " + (end - start) + "ms"); +// logger.info("hitboxes size: " + hitboxes.size()); if (CardCrawlGame.dungeon.player.orbs.size() > 0 && hitboxes.size() > 0) - sb_safe.append(", "); + sb_safe.append(","); - for (int i = 0; i < hitboxes.size(); i++) { + Iterator hb_iter = hitboxes.iterator(); + Iterator> pt_iter = tip_lists.iterator(); - Hitbox hb = hitboxes.get(i); - ArrayList tips = tip_lists.get(i); + while (hb_iter.hasNext() && pt_iter.hasNext()) { + Hitbox hb = hb_iter.next(); + ArrayList tips = pt_iter.next(); float x = hb.x / Settings.WIDTH * 100f; float y = (Settings.HEIGHT - hb.y - hb.height) / Settings.HEIGHT* 100f; // invert the y-coordinate float w = hb.width / Settings.WIDTH * 100f; float h = hb.height / Settings.HEIGHT * 100f; - sb_safe.append(String.format(Locale.US, "{\"hitbox\": {\"x\": %.3f, \"y\": %.3f, \"w\": %.3f, \"h\": %.3f}, ", x, y, w, h)); - - sb_safe.append("\"power_tips\": "); + sb_safe.append(String.format(Locale.US, "[%.2f,%.2f,%.2f,%.2f,", x, y, w, h)); buildPowerTips(sb_safe, tips); - sb_safe.append('}'); + sb_safe.append(']'); - if (i < hitboxes.size() - 1) - sb_safe.append(", "); + if (hb_iter.hasNext() && pt_iter.hasNext()) + sb_safe.append(","); } result = sb_safe.toString(); } catch(Exception e) { @@ -187,68 +188,6 @@ private void buildCustomTips(StringBuilder sb) { sb.append(']'); } - private static ArrayList> sanitizePowerTipsLists(ArrayList> tip_lists) { - ArrayList> new_tip_lists = new ArrayList<>(); - - for (int i = 0; i < tip_lists.size(); i++) { - new_tip_lists.add(new ArrayList<>()); - for (int j = 0; j < tip_lists.get(i).size(); j++) { - new_tip_lists.get(i).add(new PowerTip(tip_lists.get(i).get(j).header, tip_lists.get(i).get(j).body)); - } - } - - return new_tip_lists; - } - - private Object[] getTipsFromMods() { - Object[] result = new Object[2]; - - ArrayList hitboxes = new ArrayList<>(); - ArrayList> tip_lists = new ArrayList<>(); - - ArrayList> classes = SlayTheRelicsExporter.instance.customTipImplementingClasses; - - for (int i = 0; i < classes.size(); i++) { - Class cls = classes.get(i); - - try { - - logger.info("class: " + cls.getCanonicalName()); - - ArrayList mod_hitboxes = (ArrayList) cls.getField(SlayTheRelicsExporter.CUSTOM_TIP_HITBOX_NAME).get(null); - ArrayList> mod_tip_lists = (ArrayList>) cls.getField(SlayTheRelicsExporter.CUSTOM_TIP_POWERTIPS_NAME).get(null); - - for (int j = 0; j < mod_hitboxes.size(); j++) { - Hitbox hb = mod_hitboxes.get(j); - logger.info(String.format("hitbox %d: %f %f %f %f", j, hb.x, hb.y, hb.width, hb.height)); - } - - for (int j = 0; j < mod_tip_lists.size(); j++) { - logger.info(String.format("tips list %d", j)); - for (int k = 0; k < mod_tip_lists.get(j).size(); k++) { - PowerTip tip = mod_tip_lists.get(j).get(k); - logger.info(String.format("tip %d: %s, %s", k, tip.header, tip.body)); - } - } - - if (mod_hitboxes.size() == mod_tip_lists.size()) { - logger.info("found fields, adding " + mod_hitboxes.size() + " entries"); - hitboxes.addAll(mod_hitboxes); - tip_lists.addAll(sanitizePowerTipsLists(mod_tip_lists)); - } else { - logger.info("hitboxes and powertip list don't have the same size for class: " + cls.getCanonicalName()); - } - - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - } - - result[0] = hitboxes; - result[1] = tip_lists; - return result; - } - private void buildOrbTips(StringBuilder sb) { ArrayList orbs = CardCrawlGame.dungeon.player.orbs; for (int i = 0; i < orbs.size(); i++) { @@ -258,16 +197,15 @@ private void buildOrbTips(StringBuilder sb) { float y = (Settings.HEIGHT - orb.hb.y - orb.hb.height) / Settings.HEIGHT* 100f; // invert the y-coordinate float w = orb.hb.width / Settings.WIDTH * 100f; float h = orb.hb.height / Settings.HEIGHT * 100f; - sb.append(String.format(Locale.US, "{\"hitbox\": {\"x\": %.3f, \"y\": %.3f, \"w\": %.3f, \"h\": %.3f}, ", x, y, w, h)); + sb.append(String.format(Locale.US, "[%.2f,%.2f,%.2f,%.2f,", x, y, w, h)); - sb.append("\"power_tips\": "); ArrayList tips = new ArrayList<>(); tips.add(new PowerTip(orb.name, orb.description)); buildPowerTips(sb, tips); - sb.append('}'); + sb.append(']'); if (i < orbs.size() - 1) - sb.append(", "); + sb.append(","); } } @@ -318,14 +256,14 @@ private void buildPowerTips(StringBuilder sb, ArrayList tips) { sb.append(getPowerTipIndex(tip)); if (i < tips.size() - 1) - sb.append(", "); + sb.append(","); } sb.append(']'); } private void buildPotions(StringBuilder sb) { - sb.append("{\"potion_x\": " + (int) (TopPanel.potionX / Settings.scale) + ", \"items\": ["); + sb.append("[" + (int) (TopPanel.potionX / Settings.scale) + ",["); if (CardCrawlGame.isInARun() && CardCrawlGame.dungeon != null && CardCrawlGame.dungeon.player != null) { ArrayList potions = CardCrawlGame.dungeon.player.potions; @@ -333,10 +271,10 @@ private void buildPotions(StringBuilder sb) { buildPotion(sb, potions.get(i)); if (i < potions.size() - 1) - sb.append(", "); + sb.append(","); } } - sb.append("]}"); + sb.append("]]"); } private void buildPotion(StringBuilder sb, AbstractPotion potion) { @@ -347,7 +285,7 @@ private void buildPotion(StringBuilder sb, AbstractPotion potion) { sb.append(getPowerTipIndex(tip)); if (i < potion.tips.size() - 1) { - sb.append(", "); + sb.append(","); } } sb.append(']'); @@ -364,15 +302,15 @@ private void buildRelics(StringBuilder sb) { int first_index = pageId * MAX_RELICS; int last_index = Math.min((pageId + 1) * MAX_RELICS, relics.size()); - sb.append("{\"is_relics_multipage\": \"" + (relics.size() > MAX_RELICS) + "\", "); - sb.append("\"items\": ["); + sb.append("[" + ((relics.size() > MAX_RELICS) ? 1:0) + ","); + sb.append("["); for (int i = first_index; i < last_index; i++) { buildRelic(sb, relics.get(i)); if (i < last_index - 1) - sb.append(", "); + sb.append(","); } - sb.append("]}"); + sb.append("]]"); } private void buildRelic(StringBuilder sb, AbstractRelic relic) { @@ -384,21 +322,21 @@ private void buildRelic(StringBuilder sb, AbstractRelic relic) { sb.append(getPowerTipIndex(tip)); if (i < relic.tips.size() - 1) { - sb.append(", "); + sb.append(","); } } sb.append(']'); } private void buildPowerTips(StringBuilder sb) { - sb.append('['); + sb.append('\"'); for (int i = 0; i < powerTips.size(); i++) { sb.append(powerTips.get(i)); if (i < powerTips.size() - 1) - sb.append(", "); + sb.append(";;"); } - sb.append(']'); + sb.append('\"'); } private int getPowerTipIndex(PowerTip tip) { @@ -446,7 +384,7 @@ private static String getImageCode(Texture img) { private static String powerTipJson(String header, String body) { return String.format( - "{\"name\": \"%s\", \"description\": \"%s\"}", + "%s;%s", sanitize(header), sanitize(body) ); @@ -454,7 +392,7 @@ private static String powerTipJson(String header, String body) { private static String powerTipJson(String header, String body, String img) { return String.format( - "{\"name\": \"%s\", \"description\": \"%s\", \"img\": \"%s\"}", + "%s;%s;%s", sanitize(header), sanitize(body), sanitize(img) @@ -466,12 +404,7 @@ private static String sanitize(String str) { return ""; str = str.replace("\"", "\\\""); -// str = str.replace("[R]", "[E]"); -// str = str.replace("[G]", "[E]"); -// str = str.replace("[B]", "[E]"); -// str = str.replace("[W]", "[E]"); str = str.replaceAll("\\[[A-Z]\\]", "[E]"); - str = str.replace("NL", "
"); return str; } diff --git a/src/main/java/str_exporter/SlayTheRelicsExporter.java b/src/main/java/str_exporter/SlayTheRelicsExporter.java index 10d50b6..36dc80e 100644 --- a/src/main/java/str_exporter/SlayTheRelicsExporter.java +++ b/src/main/java/str_exporter/SlayTheRelicsExporter.java @@ -52,18 +52,12 @@ public class SlayTheRelicsExporter implements private static String secret = null; private static String version = ""; - private long lastBroadcast = System.currentTimeMillis(); private long lastCheck = System.currentTimeMillis(); - private String lastBroadcastJson = ""; private boolean checkNextUpdate = false; private JSONMessageBuilder json_builder; - public static final String CUSTOM_TIP_HITBOX_NAME = "slayTheRelicsHitboxes"; - public static final String CUSTOM_TIP_POWERTIPS_NAME = "slayTheRelicsPowerTips"; - public ArrayList> customTipImplementingClasses; - // private static final long MAX_BROADCAST_PERIOD_MILLIS = 250; - private static final long MAX_CHECK_PERIOD_MILLIS = 2500; + private static final long MAX_CHECK_PERIOD_MILLIS = 100; public static SlayTheRelicsExporter instance = null; public static Properties strDefaultSettings = new Properties(); @@ -103,54 +97,6 @@ public static String getVersion() { return "unkwnown"; } - public static ArrayList> findCustomTipImplementingClasses() { - logger.info("CHECKING FOR OTHER MODS"); - - ArrayList> implementingClasses = new ArrayList<>(); - - ClassLoader loader = SlayTheRelicsExporter.class.getClassLoader(); - - for (ModInfo info : Loader.MODINFOS) { - if (Patcher.annotationDBMap.containsKey(info.jarURL)) { - Set initializers = Patcher.annotationDBMap.get(info.jarURL).getAnnotationIndex().get(SpireInitializer.class.getName()); - if (initializers != null) { - System.out.println(" - " + info.Name); - for (String initializer : initializers) { - System.out.println(" - " + initializer); - try { - long startTime = System.nanoTime(); - - Class c = loader.loadClass(initializer); - - try { - Field hitboxes = c.getField(CUSTOM_TIP_HITBOX_NAME); - Field powerTips = c.getField(CUSTOM_TIP_POWERTIPS_NAME); - - if (hitboxes.getType() == ArrayList.class && powerTips.getType() == ArrayList.class) { - implementingClasses.add(c); - logger.info("Fields found for class " + c.getCanonicalName()); - } - } catch (NoSuchFieldException e) { - logger.info("No fields found for class " + c.getCanonicalName()); -// e.printStackTrace(); - } - - long endTime = System.nanoTime(); - long duration = endTime - startTime; - System.out.println(" - " + (duration / 1000000) + "ms"); - } catch (ClassNotFoundException e) { - System.out.println("WARNING: Unable to find method initialize() on class marked @SpireInitializer: " + initializer); - } - } - } - } else { - System.err.println(info.jarURL + " Not in DB map. Something is very wrong"); - } - } - - return implementingClasses; - } - public static void initialize() { logger.info("initialize() called!"); @@ -164,6 +110,8 @@ public static void initialize() login = lines[0].split(":")[1]; secret = lines[1].split(":")[1]; + logger.info("slaytherelics_config.txt was succesfully loaded"); + // logger.info("loaded login " + login + " and secret " + secret); } catch (Exception e) { e.printStackTrace(); @@ -184,27 +132,17 @@ private void check() { if (areCredentialsValid()) { broadcast(); } else { - logger.info("Either your secret or your login are null. The config file has probably not loaded properly"); +// logger.info("Either your secret or your login are null. The config file has probably not loaded properly"); } } private void broadcast() { - long start = System.nanoTime(); String json = json_builder.buildJson(); long end = System.nanoTime(); - lastBroadcast = System.currentTimeMillis(); - lastBroadcastJson = json; - - long start_broadcast = System.nanoTime(); BackendBroadcaster.queueMessage(json); - long end_broadcast = System.nanoTime(); - logger.info("broadcasting relics"); - logger.info("json builder took " + (end - start) / 1e6 + " milliseconds"); - logger.info("adding to broadcast queue took " + (end_broadcast - start_broadcast) / 1e6 + " milliseconds"); -// logger.info(removeSecret(json)); - +// logger.info("json builder took " + (end - start) / 1e6 + " milliseconds"); } @Override @@ -213,7 +151,7 @@ public void receivePostInitialize() { ModLabel label1 = new ModLabel("Use the slider below to set encoding delay of your PC.", 400.0f, 700.0f, settingsPanel, (me) -> {}); ModLabel label2 = new ModLabel("With this set to 0, the extension will be ahead of what the stream displays.", 400.0f, 650.0f, settingsPanel, (me) -> {}); - ModSlider slider = new ModSlider("Delay", 500f, 600, 3000f, "ms", settingsPanel, (me) -> { + ModSlider slider = new ModSlider("Delay", 500f, 600, 10000f, "ms", settingsPanel, (me) -> { logger.info("slider value: " + me.value); delay = (long) (me.value * me.multiplier); }); @@ -240,8 +178,6 @@ public void receivePostInitialize() { "LordAddy", "This mod exports data to Slay the Relics Twitch extension. See the extension config on Twitch for setup instructions.", settingsPanel); - - customTipImplementingClasses = findCustomTipImplementingClasses(); } @Override @@ -256,47 +192,40 @@ public void receivePostRender(SpriteBatch spriteBatch) { } public void relicPageChanged() { - logger.info("Relic Page Changed"); queue_check(); } @Override public void receiveRelicGet(AbstractRelic abstractRelic) { - logger.info("Relic Acquired"); queue_check(); } @Override public void receivePotionGet(AbstractPotion abstractPotion) { - logger.info("Potion Acquired"); queue_check(); } @Override public void receiveStartGame() { - logger.info("StartGame received"); queue_check(); } @Override public void receivePostCreateStartingRelics(AbstractPlayer.PlayerClass playerClass, ArrayList arrayList) { - logger.info("PostCreateStartingRelics received"); queue_check(); } @Override public void receivePowersModified() { - logger.info("Powers modified"); queue_check(); } @Override public void receivePostPowerApplySubscriber(AbstractPower abstractPower, AbstractCreature abstractCreature, AbstractCreature abstractCreature1) { - logger.info("PostPowerApply received"); queue_check(); } - private static String removeSecret(String str) { + public static String removeSecret(String str) { String pattern = "\"secret\": \"[a-z0-9]*\""; return str.replaceAll(pattern, "\"secret\": \"********************\""); } diff --git a/src/main/java/str_exporter/StringCompression.java b/src/main/java/str_exporter/StringCompression.java new file mode 100644 index 0000000..157e211 --- /dev/null +++ b/src/main/java/str_exporter/StringCompression.java @@ -0,0 +1,137 @@ +package str_exporter; + +import java.util.*; + +public class StringCompression { + + private static final String WILDCARDS = "0123456789abcdefghijklmnopqrstvwxyzABCDEFGHIJKLMNOPQRSTVWXYZ_`[]/^%?@><=-+*:;,.()#$!'{}~"; + + private static class Word implements Comparable { + public String word; + public int costSave; + public LinkedList overlaps; + public boolean selected; + + public Word(String word, int costSave) { + this.word = word; + this.costSave = costSave; + this.selected = false; + this.overlaps = new LinkedList<>(); + } + + public void addOverlap(Word word) { + overlaps.add(word); + } + + public boolean canBeSelected() { + for (Word word : overlaps) { + if (word.selected) + return false; + } + return true; + } + + @Override + public int compareTo(Word o) { + return Integer.compare(costSave, o.costSave); + } + } + + public static String compress(String s) { + + long start = System.nanoTime(); + + int uncompressedLength = s.length(); + + s = s.replace('|', ':').replace('&', ':'); + ArrayList compressionDict = new ArrayList<>(); + + int[] ns = {1, 10, 9, 8, 7, 6, 5, 4, 3, 2}; + +// int n = 1; + for (int i = 0; i < ns.length && compressionDict.size() < WILDCARDS.length(); i++) { +// System.out.printf("n=%d\n", n); + + String[] parts = s.split(" "); + int n = Math.min(ns[i], parts.length); + + String[] ngrams = new String[parts.length - n + 1]; + + HashMap costSave = new HashMap<>(5); + // create list of all n-grams + for (int j = 0; j < parts.length - n + 1; j++) { + StringBuilder ngram = new StringBuilder(); + for (int k = 0; k < n; k++) { + ngram.append(parts[j + k]); + if (k < n - 1) { + ngram.append(' '); + } + } + + ngrams[j] = ngram.toString(); + } + + // compute cost saving of compressing each n-gram + for (int j = 0; j < ngrams.length; j++) { + String word = ngrams[j]; + + if (!costSave.containsKey(word)) { + costSave.put(word, new Word(word, -4)); + } else { + costSave.get(word).costSave += word.length() - 2; + } + } + + for (int j = 0; j < ngrams.length; j++) { + + Word word = costSave.get(ngrams[j]); + for (int k = Math.max(0, j - n + 1); k < Math.min(ngrams.length, j + n); k++) { + if (k != j) { + word.addOverlap(costSave.get(ngrams[k])); + } + } + } + + // sort all n-grams by cost save in decreasing order + Word[] Words = costSave.values().toArray(new Word[0]); + Arrays.sort(Words, Collections.reverseOrder()); + + // compress all net cost positive n-grams and move to n+=1 + for (int j = 0; j < Words.length; j++) { + if (Words[j].costSave > 0 && Words[j].canBeSelected()) { +// System.out.printf("word \"%s\" cost save %d wildcard &%c\n", NGrams[j].word, NGrams[j].costSave, WILDCARDS.charAt(compressionDict.size())); + s = s.replace(Words[j].word, "&" + WILDCARDS.charAt(compressionDict.size())); + compressionDict.add(Words[j].word); + Words[j].selected = true; + } else { + break; + } + + if (compressionDict.size() == WILDCARDS.length()) + break; + } + } + + StringBuilder sb = new StringBuilder(); + + // prepend the compression dictionary + for (int i = 0; i < compressionDict.size(); i++) { + sb.append(compressionDict.get(i)); + sb.append('|'); + } + + sb.append('|'); + if (compressionDict.size() == 0) // case of compressing empty message + sb.append('|'); + + sb.append(s); + + s = sb.toString(); + int compressedLength = s.length(); + + long end = System.nanoTime(); + +// SlayTheRelicsExporter.logger.info(String.format("compression, original len: %s new len: %s ratio %.2f, duration %.2f ms", uncompressedLength, compressedLength, compressedLength * 1f / uncompressedLength, (end-start)/1e6)); + return s; + } +} \ No newline at end of file