Getters/setters for internal representation, compacter tellraw generator output
- Prevent access to the internal variables of InteractiveMessagePart and TextMessagePart to ensure null safety for certain fields - More compact TellrawGenerator output (and adjusted test cases for that) - Ingame testing suite: - Use -DtestIngame="/path/to/test/server" in the maven command line to build a test plugin and copy it to the server - Start ingame testing using the 'testinteractivemessenger' command (alias: 'tim') - 'tim start [regex]': start testing all testcases of which the name matches the regex - 'tim stop': stop the running test - 'tim correct': mark the currently shown test case as correct - 'tim wrong': mark the currently shown test case as wrong - 'tim continue': continue to the next test case without giving a verdict (either because the test suite already failed the test or because you want to skip it) - The input and output of the test case is shown to the test player, and detailed information is displayed in the console - Added UTF8 encoding test to ensure all languages work - Use EnumSet where possible - Move all test cases to the resources folder, where they should be
This commit is contained in:
1 parent
f4c20c24ae
commit
2c2c73f56a
63 files changed
+1641
-595
No files matched your search
@@ -73,6 +73,101 @@
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Run ingame tests using a plugin, active using: -DtestIngame="/path/to/test/server" -->
|
||||
<!-- Don't use the 'install' command if this option is used -->
|
||||
<profile>
|
||||
<id>test-ingame</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>testIngame</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<directory>target</directory>
|
||||
<finalName>InteractiveMessengerTest</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>.</targetPath>
|
||||
<directory>src/test/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-sources</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${basedir}/src/test/java</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<!-- Relocate GSON -->
|
||||
<relocation>
|
||||
<pattern>com.google.gson</pattern>
|
||||
<shadedPattern>me.wiefferink.interactivemessenger.shaded.gson</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.8</version>
|
||||
<configuration>
|
||||
<target>
|
||||
<copy file="./target/${project.build.finalName}.jar" tofile="${testIngame}\${project.build.finalName}.jar"/>
|
||||
</target>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Produce javadoc files, activate by using -Djavadoc=path/to/directory -->
|
||||
<profile>
|
||||
<id>generate-javadoc</id>
|
||||
|
||||
@@ -46,7 +46,6 @@ public class ConsoleGenerator {
|
||||
put(Format.OBFUSCATE, 'k');
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given message to a String containing control characters
|
||||
* for formatting that can be used for console outputs, but also for normal player
|
||||
@@ -64,26 +63,21 @@ public class ConsoleGenerator {
|
||||
for(InteractiveMessagePart interactivePart : message) {
|
||||
for(TextMessagePart textPart : interactivePart) {
|
||||
// Use reset if there is formatting active we need to get rid of
|
||||
if(!textPart.formatting.containsAll(activeFormatting)) {
|
||||
if(!textPart.getFormatting().containsAll(activeFormatting)) {
|
||||
result.append(ChatColor.RESET);
|
||||
activeColor = Color.WHITE;
|
||||
activeFormatting.clear();
|
||||
}
|
||||
// If there is no incoming color and it is not already white set it to white
|
||||
else if(textPart.color == null && activeColor != Color.WHITE) {
|
||||
result.append(ChatColor.WHITE);
|
||||
activeColor = Color.WHITE;
|
||||
}
|
||||
|
||||
// Color
|
||||
if(textPart.color != null && activeColor != textPart.color) {
|
||||
result.append(ChatColor.COLOR_CHAR).append(colorCode.get(textPart.color));
|
||||
activeColor = textPart.color;
|
||||
if(activeColor != textPart.getColor()) {
|
||||
result.append(ChatColor.COLOR_CHAR).append(colorCode.get(textPart.getColor()));
|
||||
activeColor = textPart.getColor();
|
||||
}
|
||||
|
||||
// Formatting
|
||||
Set<Format> formattingToAdd = EnumSet.noneOf(Format.class);
|
||||
formattingToAdd.addAll(textPart.formatting);
|
||||
formattingToAdd.addAll(textPart.getFormatting());
|
||||
formattingToAdd.removeAll(activeFormatting);
|
||||
for(Format format : formattingToAdd) {
|
||||
result.append(ChatColor.COLOR_CHAR).append(formatCode.get(format));
|
||||
@@ -91,12 +85,12 @@ public class ConsoleGenerator {
|
||||
}
|
||||
|
||||
// Text
|
||||
result.append(textPart.text);
|
||||
result.append(textPart.getText());
|
||||
}
|
||||
|
||||
// Add newlines
|
||||
if (interactivePart.newline) {
|
||||
result.append("\n");
|
||||
if(interactivePart.hasNewline()) {
|
||||
result.append("\n");
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
|
||||
+116
-44
@@ -41,6 +41,40 @@ public class TellrawGenerator {
|
||||
put(Hover.HOVER, "show_text");
|
||||
}};
|
||||
|
||||
/* Thoughts about printing JSON that is as small as possible
|
||||
*
|
||||
* Message structure:
|
||||
* InteractiveMessage:
|
||||
* line (groups of InteractiveMessageParts without newlines, except possibly at the end)
|
||||
* InteractiveMessageParts:
|
||||
* TextMessagePart
|
||||
* more...
|
||||
* more...
|
||||
* more...
|
||||
*
|
||||
* What to do:
|
||||
* InteractiveMessage:
|
||||
* - create list to hold the lines
|
||||
* - print the lines to this list
|
||||
* line:
|
||||
* - single InteractiveMessagePart: print the part
|
||||
* - multiple InteractiveMessageParts: print an array with the parts
|
||||
* InteractiveMessagePart:
|
||||
* - single TextMessagePart: print the part
|
||||
* - multiple TextMessageParts: print an array with the parts
|
||||
* - with hover/click wrap like this: {text: "", extra: <TextMessageParts>, hoverEvent: <>, clickEvent: <>}
|
||||
* TextMessagePart:
|
||||
* - without formatting: "<text>"
|
||||
* - with formatting: {text: <text>, bold: <boolean>, color: <color>, ...}
|
||||
*
|
||||
* Take into account:
|
||||
* - when a line has a single InteractiveMessagePart, without hover/click, which has a single TextMessagePart
|
||||
* without formatting it will be printed as simple string and needs to be wrapped in an array
|
||||
* (/tellraw "hi" does not work)
|
||||
* - If there is only one TextMessagePart it might be cobined with his InteractiveMessagePart parent
|
||||
* (this is challenging in practice though)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses the given message to a JSON array that can be
|
||||
@@ -49,32 +83,39 @@ public class TellrawGenerator {
|
||||
* @return JSON string that can be send to a player (multiple means line breaks have been used)
|
||||
*/
|
||||
public static List<String> generate(InteractiveMessage message) {
|
||||
message = message.copy(); // Don't destroy the message
|
||||
message = message.copy();
|
||||
// Resulting JSON strings, each one should be printed on a new line (separate /tellraw command to provide 1.7 compatibility)
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
List<InteractiveMessagePart> combine = new ArrayList<>(); // Part that are combined to a line
|
||||
// Combine InteractiveMessageParts without newlines into a group
|
||||
List<InteractiveMessagePart> combine = new ArrayList<>();
|
||||
while(!message.isEmpty()) {
|
||||
InteractiveMessagePart part = message.removeFirst();
|
||||
combine.add(part);
|
||||
if(part.newline || message.isEmpty()) {
|
||||
// Skip empty newline parts
|
||||
if(!(part.hasNewline() && part.isEmpty())) {
|
||||
combine.add(part);
|
||||
}
|
||||
// If we need to go to the next line or we got all the parts, print it
|
||||
if(part.hasNewline() || message.isEmpty()) {
|
||||
String lineResult = "\"\"";
|
||||
if(combine.size() == 1) {
|
||||
StringBuilder nextLine = new StringBuilder();
|
||||
toJSON(combine.get(0), nextLine);
|
||||
result.add(nextLine.toString());
|
||||
lineResult = toJSON(combine.get(0), new StringBuilder()).toString();
|
||||
} else if(combine.size() > 1) {
|
||||
StringBuilder nextLine = new StringBuilder("{\"text\":\"\",\"extra\":[");
|
||||
for(int i = 0; i < combine.size(); i++) {
|
||||
// Skip possibly last empty InteractiveMessagePart which has newline set
|
||||
if(!combine.get(i).isEmpty()) {
|
||||
if(i != 0) {
|
||||
nextLine.append(",");
|
||||
}
|
||||
toJSON(combine.get(i), nextLine);
|
||||
if(i != 0) {
|
||||
nextLine.append(",");
|
||||
}
|
||||
toJSON(combine.get(i), nextLine);
|
||||
}
|
||||
nextLine.append("]}");
|
||||
result.add(nextLine.toString());
|
||||
lineResult = nextLine.toString();
|
||||
}
|
||||
// Handle bare string case (wrap in array)
|
||||
if(lineResult.charAt(0) == '"' && lineResult.charAt(lineResult.length()-1) == '"') {
|
||||
lineResult = '['+lineResult+']';
|
||||
}
|
||||
result.add(lineResult);
|
||||
combine.clear();
|
||||
}
|
||||
}
|
||||
@@ -84,91 +125,122 @@ public class TellrawGenerator {
|
||||
|
||||
/**
|
||||
* Get a JSON component for this message part
|
||||
* @param part The InteractiveMessagePart to be printed
|
||||
* @param sb The StringBuilder to append the result to
|
||||
* @return The StringBuilder where the JSON has been appended to
|
||||
*/
|
||||
private static StringBuilder toJSON(InteractiveMessagePart part, StringBuilder sb) {
|
||||
// Error case, should never happen, print something as safeguard
|
||||
if(part.size() == 0) {
|
||||
sb.append("{\"text\":\"\"}");
|
||||
sb.append("\"\"");
|
||||
return sb;
|
||||
}
|
||||
|
||||
// Only wrap if there are interactive parts to be added
|
||||
String finalCloser = "";
|
||||
String arrayCloser = "";
|
||||
boolean isInExtra = false;
|
||||
if(part.isInteractive() || part.size() > 1) {
|
||||
sb.append("{\"text\":");
|
||||
finalCloser = "}";
|
||||
isInExtra = true;
|
||||
}
|
||||
|
||||
// Add TextMessageParts
|
||||
if(part.size() == 1) {
|
||||
// Add attributes to TextMessagePart object
|
||||
if(part.getFirst().hasFormatting()) {
|
||||
if(isInExtra) {
|
||||
sb.append("\"\",\"extra\":");
|
||||
}
|
||||
}
|
||||
toJSON(part.getFirst(), sb);
|
||||
sb.deleteCharAt(sb.length()-1);
|
||||
} else {
|
||||
sb.append('{');
|
||||
sb.append("\"text\":\"\",\"extra\":[");
|
||||
if(!isInExtra) {
|
||||
sb.append("{\"text\":");
|
||||
}
|
||||
sb.append("\"\",\"extra\":[");
|
||||
for(TextMessagePart textPart : part) {
|
||||
toJSON(textPart, sb);
|
||||
sb.append(',');
|
||||
}
|
||||
sb.deleteCharAt(sb.length()-1);
|
||||
sb.append(']');
|
||||
sb.deleteCharAt(sb.length()-1); // Remove trailing comma
|
||||
sb.append("]");
|
||||
if(!isInExtra) {
|
||||
sb.append("}");
|
||||
}
|
||||
}
|
||||
sb.append(arrayCloser);
|
||||
|
||||
// Add click action
|
||||
if(part.onClick != null) {
|
||||
if(part.getOnClick() != null) {
|
||||
sb.append(',');
|
||||
sb.append("\"clickEvent\":{");
|
||||
sb.append("\"action\":\"").append(clickJsonKey.get(part.onClick)).append("\",");
|
||||
sb.append("\"action\":\"");
|
||||
sb.append(clickJsonKey.get(part.getOnClick()));
|
||||
sb.append("\",");
|
||||
sb.append("\"value\":");
|
||||
printJsonString(part.clickContent, sb);
|
||||
printJsonString(part.getOnClickContent(), sb);
|
||||
sb.append('}');
|
||||
}
|
||||
|
||||
// Add hover action
|
||||
if(part.onHover != null) {
|
||||
if(part.getOnHover() != null && !part.getOnHoverContent().isEmpty()) {
|
||||
sb.append(',');
|
||||
sb.append("\"hoverEvent\":{");
|
||||
sb.append("\"action\":\"").append(hoverJsonKey.get(part.onHover)).append("\",");
|
||||
sb.append("\"action\":\"");
|
||||
sb.append(hoverJsonKey.get(part.getOnHover()));
|
||||
sb.append("\",");
|
||||
sb.append("\"value\":");
|
||||
if(part.hoverContent.size() == 1) {
|
||||
TextMessagePart hoverPart = part.hoverContent.getFirst();
|
||||
if(hoverPart.hasFormatting()) {
|
||||
toJSON(hoverPart, sb);
|
||||
} else {
|
||||
printJsonString(hoverPart.text, sb);
|
||||
}
|
||||
if(part.getOnHoverContent().size() == 1) {
|
||||
toJSON(part.getOnHoverContent().getFirst(), sb);
|
||||
} else {
|
||||
sb.append('[');
|
||||
for(TextMessagePart hoverPart : part.hoverContent) {
|
||||
sb.append("{\"text\":\"\",\"extra\":[");
|
||||
for(TextMessagePart hoverPart : part.getOnHoverContent()) {
|
||||
toJSON(hoverPart, sb);
|
||||
sb.append(',');
|
||||
}
|
||||
sb.deleteCharAt(sb.length()-1);
|
||||
sb.append(']');
|
||||
sb.deleteCharAt(sb.length()-1); // Remove trailing comma
|
||||
sb.append("]}");
|
||||
}
|
||||
sb.append('}');
|
||||
}
|
||||
sb.append('}');
|
||||
|
||||
sb.append(finalCloser);
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON component for this message part
|
||||
* @param part The TextMessagePart to print
|
||||
* @param sb The StringBuilder to append the JSON result to
|
||||
* @return The StringBuilder where the result has been appended to
|
||||
*/
|
||||
private static StringBuilder toJSON(TextMessagePart part, StringBuilder sb) {
|
||||
// Simple string
|
||||
if(!part.hasFormatting()) {
|
||||
printJsonString(part.getText(), sb);
|
||||
return sb;
|
||||
}
|
||||
|
||||
// String with formatting
|
||||
sb.append('{');
|
||||
|
||||
// Text
|
||||
sb.append("\"text\":");
|
||||
printJsonString(part.text, sb);
|
||||
printJsonString(part.getText(), sb);
|
||||
|
||||
// Color
|
||||
if(part.color != null && part.color != Color.WHITE) {
|
||||
sb.append(",\"color\":\"").append(part.color.name().toLowerCase()).append("\"");
|
||||
if(part.getColor() != Color.WHITE) {
|
||||
sb.append(",\"color\":\"");
|
||||
sb.append(part.getColor().name().toLowerCase());
|
||||
sb.append("\"");
|
||||
}
|
||||
|
||||
// Formatting
|
||||
for(Format formatting : part.formatting) {
|
||||
for(Format formatting : part.getFormatting()) {
|
||||
sb.append(",\"");
|
||||
sb.append(formatJsonKey.get(formatting)).append("\":");
|
||||
sb.append("true");
|
||||
sb.append(formatJsonKey.get(formatting));
|
||||
sb.append("\":true");
|
||||
}
|
||||
|
||||
sb.append('}');
|
||||
|
||||
+135
-6
@@ -4,6 +4,7 @@ import me.wiefferink.interactivemessenger.message.enums.Click;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Hover;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -11,15 +12,143 @@ import java.util.Objects;
|
||||
*/
|
||||
public class InteractiveMessagePart extends LinkedList<TextMessagePart> {
|
||||
|
||||
public boolean newline = false;
|
||||
private boolean newline = false;
|
||||
|
||||
// Click
|
||||
public Click onClick = null;
|
||||
public String clickContent = "";
|
||||
private Click onClick = null;
|
||||
private String clickContent = null;
|
||||
|
||||
// Hover
|
||||
public Hover onHover = null;
|
||||
public LinkedList<TextMessagePart> hoverContent = null;
|
||||
private Hover onHover = null;
|
||||
private LinkedList<TextMessagePart> hoverContent = null;
|
||||
|
||||
/**
|
||||
* Start a new line after this part
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart newline() {
|
||||
newline = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't start a new line after this part
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart noNewline() {
|
||||
newline = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or don't start a new line after this part
|
||||
* @param newline true to start a new line after this part, otherwise false
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart newline(boolean newline) {
|
||||
this.newline = newline;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the next part should start on a new line
|
||||
* @return true if the next part should start on a new line, otherwise false
|
||||
*/
|
||||
public boolean hasNewline() {
|
||||
return newline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the click action
|
||||
* @param onClick The click action to use or null for none
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart onClick(Click onClick) {
|
||||
this.onClick = onClick;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the click action
|
||||
* @return The click action or null for none
|
||||
*/
|
||||
public Click getOnClick() {
|
||||
return onClick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the click content
|
||||
* @param clickContent The content used by the click action
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart onClickContent(String clickContent) {
|
||||
if(clickContent == null) {
|
||||
clickContent = "";
|
||||
}
|
||||
this.clickContent = clickContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the click content
|
||||
* @return The click content
|
||||
*/
|
||||
public String getOnClickContent() {
|
||||
if(clickContent == null) {
|
||||
clickContent = "";
|
||||
}
|
||||
return clickContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hover action
|
||||
* @param onHover The hover action to use or null for none
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart onHover(Hover onHover) {
|
||||
this.onHover = onHover;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hover action
|
||||
* @return The hover action or null for none
|
||||
*/
|
||||
public Hover getOnHover() {
|
||||
return onHover;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hover content
|
||||
* @param hoverContent The content used by the hover action, never null
|
||||
* @return this
|
||||
*/
|
||||
public InteractiveMessagePart onHoverContent(List<TextMessagePart> hoverContent) {
|
||||
this.hoverContent = new LinkedList<>();
|
||||
if(hoverContent != null) {
|
||||
this.hoverContent.addAll(hoverContent);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hover content
|
||||
* @return The hover content, never null
|
||||
*/
|
||||
public LinkedList<TextMessagePart> getOnHoverContent() {
|
||||
if(hoverContent == null) {
|
||||
hoverContent = new LinkedList<>();
|
||||
}
|
||||
return hoverContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are interactive actions defined
|
||||
* @return true if there is a hover or click event addd, false if it is only text
|
||||
*/
|
||||
public boolean isInteractive() {
|
||||
return onHover != null || onClick != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -28,7 +157,7 @@ public class InteractiveMessagePart extends LinkedList<TextMessagePart> {
|
||||
result.append(", onClick:").append(onClick);
|
||||
}
|
||||
if(clickContent != null && !clickContent.isEmpty()) {
|
||||
result.append(", clickContent:").append(clickContent);
|
||||
result.append(", onClickContent:").append(clickContent);
|
||||
}
|
||||
if(onHover != null) {
|
||||
result.append(", onHover:").append(onHover);
|
||||
|
||||
@@ -3,17 +3,101 @@ package me.wiefferink.interactivemessenger.message;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Color;
|
||||
import me.wiefferink.interactivemessenger.message.enums.Format;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Holds a string with basic (non-interactive) formatting.
|
||||
*/
|
||||
public class TextMessagePart {
|
||||
public String text = "";
|
||||
public Color color = null;
|
||||
public Set<Format> formatting = EnumSet.noneOf(Format.class);
|
||||
private String text = "";
|
||||
private Color color = Color.WHITE;
|
||||
private Set<Format> formatting = EnumSet.noneOf(Format.class);
|
||||
|
||||
/**
|
||||
* Set the text
|
||||
* @param text The text to set (if null text will be set to an empty string)
|
||||
* @return this
|
||||
*/
|
||||
public TextMessagePart text(String text) {
|
||||
if(text == null) {
|
||||
text = "";
|
||||
}
|
||||
this.text = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to the current text
|
||||
* @param toAppend The string to append (null is ignored)
|
||||
* @return this
|
||||
*/
|
||||
public TextMessagePart appendText(String toAppend) {
|
||||
if(toAppend != null) {
|
||||
text += toAppend;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text of this part
|
||||
* @return The text, never null
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color
|
||||
* @param color The color to set (if null it defaults to white)
|
||||
* @return this
|
||||
*/
|
||||
public TextMessagePart color(Color color) {
|
||||
if(color == null) {
|
||||
color = Color.WHITE;
|
||||
}
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color
|
||||
* @return The color, never null
|
||||
*/
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add formatting
|
||||
* @param formatting The formatting to add
|
||||
* @return this
|
||||
*/
|
||||
public TextMessagePart format(Format... formatting) {
|
||||
if(formatting != null) {
|
||||
this.formatting.addAll(Arrays.asList(formatting));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add formatting
|
||||
* @param formatting The formatting to add
|
||||
* @return this
|
||||
*/
|
||||
public TextMessagePart format(Collection<Format> formatting) {
|
||||
if(formatting != null) {
|
||||
this.formatting.addAll(formatting);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatting of this part
|
||||
* @return The formattting of this part, never null
|
||||
*/
|
||||
public Set<Format> getFormatting() {
|
||||
return formatting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this part has formatting
|
||||
@@ -26,7 +110,7 @@ public class TextMessagePart {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder("TextMessagePart(text:"+text);
|
||||
if(color != null) {
|
||||
if(color != Color.WHITE) {
|
||||
result.append(", color:").append(color);
|
||||
}
|
||||
if(!formatting.isEmpty()) {
|
||||
|
||||
@@ -98,8 +98,8 @@ public class YamlParser {
|
||||
public static InteractiveMessage parse(List<String> input, boolean doInteractives) {
|
||||
InteractiveMessage message = new InteractiveMessage();
|
||||
|
||||
Color currentColor = null;
|
||||
Set<Format> currentFormatting = new HashSet<>();
|
||||
Color currentColor = Color.WHITE;
|
||||
Set<Format> currentFormatting = EnumSet.noneOf(Format.class);
|
||||
|
||||
lineLoop:
|
||||
for(String line : input) {
|
||||
@@ -121,15 +121,15 @@ public class YamlParser {
|
||||
messagePart = message.getLast();
|
||||
Object tag = interactiveTag.tag;
|
||||
if(tag instanceof Click) {
|
||||
messagePart.onClick = (Click)interactiveTag.tag;
|
||||
messagePart.clickContent = interactiveTag.subsequentContent;
|
||||
messagePart.onClick((Click)interactiveTag.tag);
|
||||
messagePart.onClickContent(interactiveTag.subsequentContent);
|
||||
} else if(tag instanceof Hover) {
|
||||
line = interactiveTag.subsequentContent;
|
||||
isHoverLine = true;
|
||||
if(messagePart.onHover != tag) {
|
||||
// Hover type changed
|
||||
messagePart.hoverContent = new LinkedList<>();
|
||||
messagePart.onHover = (Hover)tag;
|
||||
// Hover type changed, reset content
|
||||
if(messagePart.getOnHover() != tag) {
|
||||
messagePart.getOnHoverContent().clear();
|
||||
messagePart.onHover((Hover)tag);
|
||||
}
|
||||
// Add hover content below
|
||||
}
|
||||
@@ -142,13 +142,13 @@ public class YamlParser {
|
||||
LinkedList<TextMessagePart> targetList = messagePart;
|
||||
if(isHoverLine) {
|
||||
// Reset - use own
|
||||
currentLineColor = null;
|
||||
currentLineFormatting = new HashSet<>();
|
||||
targetList = messagePart.hoverContent;
|
||||
currentLineColor = Color.WHITE;
|
||||
currentLineFormatting = EnumSet.noneOf(Format.class);
|
||||
targetList = messagePart.getOnHoverContent();
|
||||
|
||||
// Add line break after previous hover line
|
||||
if(!targetList.isEmpty()) {
|
||||
targetList.getLast().text += '\n';
|
||||
targetList.getLast().appendText("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +168,10 @@ public class YamlParser {
|
||||
|
||||
// Add a text part with the correct formatting
|
||||
if(!textToAdd.isEmpty()) {
|
||||
TextMessagePart part = new TextMessagePart();
|
||||
part.text = unescape(textToAdd);
|
||||
part.formatting = new HashSet<>(currentLineFormatting);
|
||||
part.color = currentLineColor;
|
||||
TextMessagePart part = new TextMessagePart()
|
||||
.text(unescape(textToAdd))
|
||||
.format(currentLineFormatting)
|
||||
.color(currentLineColor);
|
||||
targetList.add(part);
|
||||
}
|
||||
|
||||
@@ -189,9 +189,9 @@ public class YamlParser {
|
||||
}
|
||||
} else if(tag == Control.BREAK) {
|
||||
if(isHoverLine && !targetList.isEmpty()) {
|
||||
targetList.getLast().text += "\n";
|
||||
targetList.getLast().appendText("\n");
|
||||
} else {
|
||||
messagePart.newline = true;
|
||||
messagePart.newline();
|
||||
currentLineFormatting.clear();
|
||||
// TODO: Remove this and support multiple line breaks and even content after a break?
|
||||
continue lineLoop;
|
||||
@@ -214,7 +214,7 @@ public class YamlParser {
|
||||
Iterator<InteractiveMessagePart> it = message.iterator();
|
||||
while(it.hasNext()) {
|
||||
InteractiveMessagePart check = it.next();
|
||||
if(check.size() == 0 && !check.newline) {
|
||||
if(check.size() == 0 && !check.hasNewline()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,10 +362,11 @@ public class Message {
|
||||
if(target instanceof CommandSender) {
|
||||
((CommandSender)target).sendMessage(plainMessage);
|
||||
} else if(target instanceof Logger) {
|
||||
((Logger)target).info(plainMessage);
|
||||
((Logger)target).info(ChatColor.stripColor(plainMessage));
|
||||
} else if(target instanceof BufferedWriter) {
|
||||
try {
|
||||
((BufferedWriter)target).write(plainMessage);
|
||||
((BufferedWriter)target).write(ChatColor.stripColor(plainMessage));
|
||||
((BufferedWriter)target).newLine();
|
||||
} catch(IOException e) {
|
||||
warn("Exception while writing to BufferedWriter:", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
|
||||
@@ -101,8 +101,6 @@ public class LanguageManager implements MessageProvider {
|
||||
while((read = input.read(bytes)) != -1) {
|
||||
output.write(bytes, 0, read);
|
||||
}
|
||||
input.close();
|
||||
output.close();
|
||||
} catch(IOException e) {
|
||||
Message.warn("Something went wrong saving a default language file: "+targetFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.Arrays;
|
||||
|
||||
public class RunTests {
|
||||
|
||||
public static final File testDirectory = new File(StringUtils.join(Arrays.asList("src", "test", "java", "me", "wiefferink", "interactivemessenger", "testing"), File.separator));
|
||||
public static final File testDirectory = new File(StringUtils.join(Arrays.asList("src", "test", "resources"), File.separator));
|
||||
public static final File parserGenerator = new File(testDirectory.getAbsolutePath()+File.separator+"parsergenerator");
|
||||
|
||||
public static Test suite() {
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package me.wiefferink.interactivemessenger.testing.ingame;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class IngameTest extends JavaPlugin {
|
||||
|
||||
public static File resourcesFolder;
|
||||
|
||||
private TestRun testRun;
|
||||
private static IngameTest instance;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
resourcesFolder = new File(IngameTest.getInstance().getDataFolder().getAbsolutePath()+File.separator+"resources");
|
||||
}
|
||||
|
||||
public static IngameTest getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if(!sender.hasPermission("interactivemessenger.test")) {
|
||||
sender.sendMessage("You don't have permission to test the InteractiveMessenger library.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(args.length == 0) {
|
||||
sender.sendMessage("/tim <start|stop|correct|wrong|continue>.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO console test version with ConsoleGenerator?
|
||||
if(!(sender instanceof Player)) {
|
||||
sender.sendMessage("Only players can run this test.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player)sender;
|
||||
|
||||
// Start test run
|
||||
if("start".equalsIgnoreCase(args[0])) {
|
||||
if(testRun != null) {
|
||||
sender.sendMessage("There is already a test running by "+testRun.getTester().getName()+".");
|
||||
return true;
|
||||
}
|
||||
|
||||
String filter = null;
|
||||
if(args.length > 1) {
|
||||
filter = args[1];
|
||||
}
|
||||
testRun = new TestRun(player, filter);
|
||||
}
|
||||
|
||||
// Set result of the case
|
||||
else {
|
||||
if(testRun == null) {
|
||||
sender.sendMessage("There is no active test run, start with '/tim start'.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!testRun.getTester().equals(player)) {
|
||||
sender.sendMessage(testRun.getTester().getName()+" is doing a test run, wait for it to be complete.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stop the test run
|
||||
if("stop".equalsIgnoreCase(args[0])) {
|
||||
sender.sendMessage("Stopped test run");
|
||||
finishTestRun();
|
||||
}
|
||||
|
||||
// Continue to the next case
|
||||
else if("continue".equalsIgnoreCase(args[0])) {
|
||||
testRun.doNextTestCase();
|
||||
}
|
||||
|
||||
// Confirm test case as correct
|
||||
else if("correct".equalsIgnoreCase(args[0])) {
|
||||
testRun.result(TestCase.TestResult.SUCCESS);
|
||||
}
|
||||
|
||||
// Confirm test case as wrong
|
||||
else if("wrong".equalsIgnoreCase(args[0])) {
|
||||
testRun.result(TestCase.TestResult.FAILED);
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the current test run
|
||||
*/
|
||||
public void finishTestRun() {
|
||||
testRun = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void info(Object... message) {
|
||||
instance.getLogger().info(ChatColor.stripColor(StringUtils.join(message, " ")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a warning to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public static void warn(Object... message) {
|
||||
instance.getLogger().warning(ChatColor.stripColor(StringUtils.join(message, " ")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error to the console
|
||||
* @param message The messagfe to print
|
||||
*/
|
||||
public static void error(Object... message) {
|
||||
instance.getLogger().severe(ChatColor.stripColor(StringUtils.join(message, " ")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package me.wiefferink.interactivemessenger.testing.ingame;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import me.wiefferink.interactivemessenger.generators.TellrawGenerator;
|
||||
import me.wiefferink.interactivemessenger.message.InteractiveMessage;
|
||||
import me.wiefferink.interactivemessenger.parsers.YamlParser;
|
||||
import me.wiefferink.interactivemessenger.testing.Log;
|
||||
import me.wiefferink.interactivemessenger.testing.parsergenerator.ParserGeneratorTestInput;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class TestCase implements Comparable<TestCase> {
|
||||
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().setLenient().create();
|
||||
|
||||
enum TestResult {
|
||||
SUCCESS,
|
||||
FAILED
|
||||
|
||||
}
|
||||
|
||||
private File testFile;
|
||||
private TestResult result;
|
||||
private TestRun testRun;
|
||||
|
||||
public TestCase(TestRun testRun, File testFile) {
|
||||
this.testRun = testRun;
|
||||
this.testFile = testFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the test case (path leading to the file)
|
||||
* @return Name of this testcase
|
||||
*/
|
||||
public String getName() {
|
||||
String result = testFile.getAbsolutePath().substring(IngameTest.resourcesFolder.getAbsolutePath().length()+1);
|
||||
if(result.startsWith("parsergenerator"+File.separator)) {
|
||||
result = result.substring(15+File.separator.length());
|
||||
}
|
||||
result = result.replace("\\", "/");
|
||||
if(result.endsWith(".txt")) {
|
||||
result = result.substring(0, result.length()-4);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the test case
|
||||
*/
|
||||
public void show() {
|
||||
// Bump away last case
|
||||
testRun.cleanScreen();
|
||||
testRun.info(getName());
|
||||
ParserGeneratorTestInput data;
|
||||
try {
|
||||
data = ParserGeneratorTestInput.from(testFile);
|
||||
} catch(IOException e) {
|
||||
testRun.error("Failed to load test data:", testFile.getAbsolutePath(), "\n", ExceptionUtils.getStackTrace(e));
|
||||
setupFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Show input
|
||||
if(data.input.isEmpty()) {
|
||||
testRun.error("Input is empty:", testFile.getAbsolutePath());
|
||||
setupFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
testRun.info("Input of", data.input.size(), "line"+(data.input.size() == 1 ? "" : "s")+":");
|
||||
for(String inputPart : data.input) {
|
||||
testRun.message(inputPart);
|
||||
}
|
||||
|
||||
// Show output
|
||||
InteractiveMessage parsedMessage = YamlParser.parse(data.input);
|
||||
List<String> tellRawOutput = TellrawGenerator.generate(parsedMessage);
|
||||
testRun.info("Output of", +tellRawOutput.size(), "line"+(tellRawOutput.size() == 1 ? "" : "s")+":");
|
||||
boolean result = true;
|
||||
int line = 1;
|
||||
for(String jsonMessage : tellRawOutput) {
|
||||
// Parse actual output into Json
|
||||
JsonElement tellRawJsonOutput;
|
||||
try {
|
||||
tellRawJsonOutput = gson.fromJson(jsonMessage, JsonElement.class);
|
||||
testRun.console("Message structure:", parsedMessage);
|
||||
testRun.console("Output line", line+":");
|
||||
Log.printIndented(2, gson.toJson(tellRawJsonOutput));
|
||||
result &= Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw "+testRun.getTester().getName()+" "+jsonMessage);
|
||||
} catch(JsonSyntaxException e) {
|
||||
result = false;
|
||||
testRun.error("Generated Json output is invalid:", e.getMessage(), ExceptionUtils.getStackTrace(e));
|
||||
testRun.error("Raw json:", jsonMessage);
|
||||
} catch(Exception e) {
|
||||
testRun.error("Exception at invoking /tellraw:", e.getMessage(), ExceptionUtils.getStackTrace(e));
|
||||
result = false;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
setupFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask if the output is correct (no hover because popup is above output)
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw "+testRun.getTester().getName()
|
||||
+" ["+
|
||||
// Prefix
|
||||
" {"+
|
||||
" \"text\": \">>> Result: \","+
|
||||
" \"color\": \"blue\","+
|
||||
" \"bold\": true"+
|
||||
" },"+
|
||||
// Correct button
|
||||
" {"+
|
||||
" \"text\": \"[Correct]\","+
|
||||
" \"color\": \"dark_green\","+
|
||||
" \"bold\": true,"+
|
||||
" \"clickEvent\": {"+
|
||||
" \"action\": \"run_command\","+
|
||||
" \"value\": \"/testinteractivemessenger correct\""+
|
||||
" }"+
|
||||
" },"+
|
||||
// Space
|
||||
" {"+
|
||||
" \"text\": \" \""+
|
||||
" },"+
|
||||
// Wrong button
|
||||
" {"+
|
||||
" \"text\": \"[Wrong]\","+
|
||||
" \"color\": \"dark_red\","+
|
||||
" \"bold\": true,"+
|
||||
" \"clickEvent\": {"+
|
||||
" \"action\": \"run_command\","+
|
||||
" \"value\": \"/testinteractivemessenger wrong\""+
|
||||
" }"+
|
||||
" },"+
|
||||
// Progress
|
||||
" {"+
|
||||
" \"text\": \" "+(testRun.getProgress()+1)+"/"+testRun.getTestCases().size()+"\","+
|
||||
" \"color\": \"blue\","+
|
||||
" \"bold\": true"+
|
||||
" }"+
|
||||
"]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show test case setup failed message with continue button
|
||||
*/
|
||||
private void setupFailed() {
|
||||
// Sending/parsing already failed, show continue button
|
||||
setResult(TestResult.FAILED);
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw "+testRun.getTester().getName()
|
||||
+" ["+
|
||||
" {"+
|
||||
" \"text\": \">>> Test case setup failed! \","+
|
||||
" \"color\": \"dark_red\","+
|
||||
" \"bold\": true"+
|
||||
" },"+
|
||||
" {"+
|
||||
" \"text\": \"[Continue]\","+
|
||||
" \"color\": \"blue\","+
|
||||
" \"bold\": true,"+
|
||||
" \"clickEvent\": {"+
|
||||
" \"action\": \"run_command\","+
|
||||
" \"value\": \"testinteractivemessenger continue\""+
|
||||
" },"+
|
||||
" \"hoverEvent\": {"+
|
||||
" \"action\": \"show_text\","+
|
||||
" \"value\": {"+
|
||||
" \"text\": \"\","+
|
||||
" \"extra\": ["+
|
||||
" {"+
|
||||
" \"text\": \"<Continue to the next test case>\","+
|
||||
" \"color\": \"blue\""+
|
||||
" }"+
|
||||
" ]"+
|
||||
" }"+
|
||||
" }"+
|
||||
" }"+
|
||||
"]");
|
||||
}
|
||||
|
||||
/**
|
||||
* The feedback of the player
|
||||
* @param result The result given by the player
|
||||
*/
|
||||
public void result(TestResult result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text result
|
||||
* @param result The result to set
|
||||
*/
|
||||
public void setResult(TestResult result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of the test
|
||||
* @return The result of the test
|
||||
*/
|
||||
public TestResult getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestCase otherTestCase) {
|
||||
return otherTestCase.getName().compareTo(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package me.wiefferink.interactivemessenger.testing.ingame;
|
||||
|
||||
import me.wiefferink.interactivemessenger.testing.Log;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class TestRun implements Listener {
|
||||
private Player tester;
|
||||
private Pattern testFilter;
|
||||
private List<TestCase> testCases;
|
||||
private int progress = 0;
|
||||
|
||||
/**
|
||||
* Constructor, setup and start the test run
|
||||
* @param tester The player that is going to test the messages
|
||||
* @param testFilter Regex to filter the tests that should be shown
|
||||
*/
|
||||
public TestRun(Player tester, String testFilter) {
|
||||
this.tester = tester;
|
||||
if(testFilter == null) {
|
||||
testFilter = ".*";
|
||||
}
|
||||
this.testFilter = Pattern.compile(testFilter);
|
||||
info("Setting up test environment...");
|
||||
Bukkit.getServer().getPluginManager().registerEvents(this, IngameTest.getInstance());
|
||||
boolean started = saveResources()
|
||||
&& createTestCases()
|
||||
&& doNextTestCase();
|
||||
if(!started) {
|
||||
IngameTest.getInstance().finishTestRun();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the test run and cleanup
|
||||
*/
|
||||
public void exit() {
|
||||
HandlerList.unregisterAll(this);
|
||||
tester = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player that is running the test
|
||||
* @return The player running the test
|
||||
*/
|
||||
public Player getTester() {
|
||||
return tester;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the progress
|
||||
* @return At which test the run is (can be 0 until getTestCases().size()-1)
|
||||
*/
|
||||
public int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test cases defined for this test run
|
||||
* @return The test cases
|
||||
*/
|
||||
public List<TestCase> getTestCases() {
|
||||
return testCases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show raw test run information
|
||||
* @param message The message to print
|
||||
*/
|
||||
public void message(Object... message) {
|
||||
tester.sendMessage(StringUtils.join(message, " "));
|
||||
IngameTest.info(prefix(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show test run information
|
||||
* @param message The message to print
|
||||
*/
|
||||
public void info(Object... message) {
|
||||
tester.sendMessage(ChatColor.BLUE+""+ChatColor.BOLD+">>> "+StringUtils.join(message, " "));
|
||||
IngameTest.info(prefix(message));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show test run information to the console
|
||||
* @param message The message to print
|
||||
*/
|
||||
public void console(Object... message) {
|
||||
IngameTest.info(prefix(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show test run error information
|
||||
* @param message The message to print
|
||||
*/
|
||||
public void error(Object... message) {
|
||||
tester.sendMessage(ChatColor.DARK_RED+""+ChatColor.BOLD+">>> "+StringUtils.join(message, " "));
|
||||
IngameTest.error(prefix(message));
|
||||
}
|
||||
|
||||
public Object[] prefix(Object... message) {
|
||||
Object[] result = new Object[message.length+1];
|
||||
System.arraycopy(message, 0, result, 1, message.length);
|
||||
result[0] = "[TestRun:"+tester.getName()+"]";
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the visible screen (20 max visible, scrolling will still show old results)
|
||||
*/
|
||||
public void cleanScreen() {
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw "+tester.getName()+" [\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\"]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Save files used in the test to disk
|
||||
*/
|
||||
private boolean saveResources() {
|
||||
try {
|
||||
// Delete old files
|
||||
deleteFolderRecursive(new File(IngameTest.getInstance().getDataFolder()+File.separator+"resources"));
|
||||
} catch(IOException e) {
|
||||
error("Deleting old resources failed:", ExceptionUtils.getStackTrace(e));
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// Read jar as ZIP file
|
||||
File jarPath = new File(IngameTest.getInstance().getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
ZipFile jar = new ZipFile(jarPath);
|
||||
Enumeration<? extends ZipEntry> entries = jar.entries();
|
||||
|
||||
// Each entry is a file or directory
|
||||
while(entries.hasMoreElements()) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
|
||||
// Filter to YAML files in the language directory
|
||||
if(!entry.isDirectory() && entry.getName().startsWith("parsergenerator/") && entry.getName().endsWith(".txt")) {
|
||||
// Save the file to disk
|
||||
File targetFile = new File(IngameTest.resourcesFolder.getAbsolutePath()+
|
||||
File.separator+
|
||||
entry.getName());
|
||||
if(!targetFile.getParentFile().exists()) {
|
||||
if(!targetFile.getParentFile().mkdirs()) {
|
||||
error("Could not create resources folder:", targetFile.getParentFile().getAbsolutePath());
|
||||
continue; // Skip this one, keep trying the rest
|
||||
}
|
||||
}
|
||||
try(
|
||||
InputStream input = jar.getInputStream(entry);
|
||||
OutputStream output = new FileOutputStream(targetFile)
|
||||
) {
|
||||
int read;
|
||||
byte[] bytes = new byte[1024];
|
||||
while((read = input.read(bytes)) != -1) {
|
||||
output.write(bytes, 0, read);
|
||||
}
|
||||
} catch(IOException e) {
|
||||
error("Something went wrong saving a test resource file:", targetFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(URISyntaxException e) {
|
||||
error("Failed to find location of jar file:", ExceptionUtils.getStackTrace(e));
|
||||
return false;
|
||||
} catch(IOException e) {
|
||||
error("Failed to read zip file:", ExceptionUtils.getStackTrace(e));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes Folder with all of its content
|
||||
* @param folder path to folder which should be deleted
|
||||
*/
|
||||
private void deleteFolderRecursive(File folder) throws IOException {
|
||||
if(!folder.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Files.walkFileTree(folder.toPath(), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
if(exc != null) {
|
||||
throw exc;
|
||||
}
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup test cases based on files on disk
|
||||
*/
|
||||
private boolean createTestCases() {
|
||||
testCases = new ArrayList<>();
|
||||
|
||||
// Recursive lookup of files in the resources folder
|
||||
addTestCases(testCases, IngameTest.resourcesFolder);
|
||||
Log.info("added test cases:", testCases);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addTestCases(List<TestCase> list, File folder) {
|
||||
File[] files = folder.listFiles();
|
||||
if(files == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(File file : files) {
|
||||
if(file.isFile()) {
|
||||
list.add(new TestCase(this, file));
|
||||
} else if(file.isDirectory()) {
|
||||
addTestCases(list, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do next test case
|
||||
*/
|
||||
public boolean doNextTestCase() {
|
||||
// Either no testcases or something wrong
|
||||
if(testCases == null || testCases.size() == 0) {
|
||||
tester.sendMessage("There are no test cases defined, check if there are any in the folder: "+IngameTest.resourcesFolder.getAbsolutePath());
|
||||
return false;
|
||||
}
|
||||
|
||||
while(true) {
|
||||
// Done, print result
|
||||
if(progress >= testCases.size()) {
|
||||
printResults();
|
||||
return true;
|
||||
}
|
||||
|
||||
TestCase next = testCases.get(progress);
|
||||
|
||||
// Filter test cases
|
||||
if(testFilter.matcher(next.getName()).matches()) {
|
||||
next.show();
|
||||
return true;
|
||||
} else {
|
||||
progress++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the result of the running test case and do the next
|
||||
* @param result Result of the current test case
|
||||
*/
|
||||
public void result(TestCase.TestResult result) {
|
||||
testCases.get(progress).result(result);
|
||||
progress++;
|
||||
doNextTestCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the results of a test run
|
||||
*/
|
||||
private void printResults() {
|
||||
cleanScreen();
|
||||
|
||||
// Collect results
|
||||
SortedSet<TestCase> success = new TreeSet<>();
|
||||
SortedSet<TestCase> skipped = new TreeSet<>();
|
||||
SortedSet<TestCase> failed = new TreeSet<>();
|
||||
for(TestCase testCase : testCases) {
|
||||
if(testCase.getResult() == TestCase.TestResult.SUCCESS) {
|
||||
success.add(testCase);
|
||||
} else if(testCase.getResult() == TestCase.TestResult.FAILED) {
|
||||
failed.add(testCase);
|
||||
} else {
|
||||
skipped.add(testCase);
|
||||
}
|
||||
}
|
||||
|
||||
// Print report
|
||||
info("Testing complete, result:");
|
||||
message("Test count: "+testCases.size());
|
||||
|
||||
if(!success.isEmpty()) {
|
||||
message(ChatColor.GREEN+"Success: "+success.size()+" test"+(success.size() != 1 ? "s" : ""));
|
||||
}
|
||||
|
||||
if(!skipped.isEmpty()) {
|
||||
message("Skipped: "+skipped.size()+" test"+(skipped.size() != 1 ? "s" : ""));
|
||||
for(TestCase testCase : skipped) {
|
||||
message(" "+testCase.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed.isEmpty()) {
|
||||
message(ChatColor.RED+"Failed: "+failed.size()+" test"+(failed.size() != 1 ? "s" : ""));
|
||||
for(TestCase testCase : failed) {
|
||||
message(" "+testCase.getName());
|
||||
}
|
||||
}
|
||||
|
||||
IngameTest.getInstance().finishTestRun();
|
||||
}
|
||||
}
|
||||
+13
-53
@@ -14,8 +14,8 @@ import me.wiefferink.interactivemessenger.testing.RunTests;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ParserGeneratorTestCase extends TestCase {
|
||||
@@ -41,70 +41,30 @@ public class ParserGeneratorTestCase extends TestCase {
|
||||
Log.info("│ Test:", RunTests.getName(file));
|
||||
Log.info("└───────────────────────────────────────────────────────────────────────────────");
|
||||
|
||||
// Read input and output from the file
|
||||
List<String> input = new ArrayList<>();
|
||||
StringBuilder expectedTellrawOutputBuilder = null;
|
||||
StringBuilder expectedConsoleOutputBuilder = null;
|
||||
boolean canIncrement = false;
|
||||
int state = 0; // 0: reading input,
|
||||
try(BufferedReader fileReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
|
||||
String line;
|
||||
while((line = fileReader.readLine()) != null) {
|
||||
// (part of) empty line barrier
|
||||
if(line.isEmpty()) {
|
||||
if(canIncrement) {
|
||||
state++;
|
||||
canIncrement = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
canIncrement = true;
|
||||
// Input line
|
||||
if(state == 0) {
|
||||
input.add(line);
|
||||
}
|
||||
// Expected tellraw output line
|
||||
else if(state == 1) {
|
||||
if(expectedConsoleOutputBuilder == null) {
|
||||
expectedConsoleOutputBuilder = new StringBuilder();
|
||||
} else {
|
||||
expectedConsoleOutputBuilder.append("\n");
|
||||
}
|
||||
expectedConsoleOutputBuilder.append(line);
|
||||
}
|
||||
// Expected console output line
|
||||
else if(state == 2) {
|
||||
if(expectedTellrawOutputBuilder == null) {
|
||||
expectedTellrawOutputBuilder = new StringBuilder();
|
||||
} else {
|
||||
expectedTellrawOutputBuilder.append("\n");
|
||||
}
|
||||
expectedTellrawOutputBuilder.append(line);
|
||||
}
|
||||
}
|
||||
ParserGeneratorTestInput data;
|
||||
try {
|
||||
data = ParserGeneratorTestInput.from(file);
|
||||
} catch(IOException e) {
|
||||
error("Failed to read file: "+ExceptionUtils.getStackTrace(e));
|
||||
return;
|
||||
}
|
||||
|
||||
String expectedConsoleOutput = expectedConsoleOutputBuilder == null ? "" : expectedConsoleOutputBuilder.toString();
|
||||
String expectedTellrawOutput = expectedTellrawOutputBuilder == null ? "" : expectedTellrawOutputBuilder.toString();
|
||||
|
||||
// Check if the input is defined (don't check output to make it easy to add tests, assertEquals will fail anyway)
|
||||
if(input.isEmpty()) {
|
||||
if(data.input.isEmpty()) {
|
||||
error("Input is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.info(" Input:");
|
||||
Log.printIndented(2, input);
|
||||
Log.printIndented(2, data.input);
|
||||
Log.info(" Expected ConsoleGenerator output:");
|
||||
Log.printIndented(2, expectedConsoleOutput);
|
||||
Log.printIndented(2, data.expectedConsoleOutput);
|
||||
Log.info(" Expected TellrawGenerator output:");
|
||||
Log.printIndented(2, expectedTellrawOutput);
|
||||
Log.printIndented(2, data.expectedTellrawOutput);
|
||||
|
||||
// Parse into InteractiveMessage
|
||||
InteractiveMessage parsedMessage = YamlParser.parse(input);
|
||||
InteractiveMessage parsedMessage = YamlParser.parse(data.input);
|
||||
Log.info(" Parsed InteractiveMessage:", parsedMessage);
|
||||
|
||||
// Generate result of ConsoleGenerator
|
||||
@@ -130,14 +90,14 @@ public class ParserGeneratorTestCase extends TestCase {
|
||||
// Parse expected output into Json
|
||||
JsonElement expectedTellrawOutputJson;
|
||||
try {
|
||||
expectedTellrawOutputJson = gson.fromJson(expectedTellrawOutput, JsonElement.class);
|
||||
expectedTellrawOutputJson = gson.fromJson(data.expectedTellrawOutput, JsonElement.class);
|
||||
} catch(JsonSyntaxException e) {
|
||||
error("Expected output is invalid Json:", ExceptionUtils.getStackTrace(e));
|
||||
return;
|
||||
}
|
||||
|
||||
// Test if equal
|
||||
assertEquals(expectedConsoleOutput, actualConsoleOutput);
|
||||
assertEquals(data.expectedConsoleOutput, actualConsoleOutput);
|
||||
assertEquals(gson.toJson(expectedTellrawOutputJson), gson.toJson(actualTellrawOutputJson));
|
||||
}
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package me.wiefferink.interactivemessenger.testing.parsergenerator;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ParserGeneratorTestInput {
|
||||
|
||||
public List<String> input;
|
||||
public String expectedConsoleOutput;
|
||||
public String expectedTellrawOutput;
|
||||
|
||||
private ParserGeneratorTestInput() {
|
||||
input = new ArrayList<>();
|
||||
expectedConsoleOutput = "";
|
||||
expectedTellrawOutput = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Load test input from a fle
|
||||
* @param file The file to load it from
|
||||
* @return ParserGeneratorTestInput with content from the file
|
||||
* @throws IOException if the file cannot be read
|
||||
*/
|
||||
public static ParserGeneratorTestInput from(File file) throws IOException {
|
||||
ParserGeneratorTestInput result = new ParserGeneratorTestInput();
|
||||
|
||||
// Read input and output from the file
|
||||
StringBuilder expectedTellrawOutputBuilder = null;
|
||||
StringBuilder expectedConsoleOutputBuilder = null;
|
||||
boolean canIncrement = false;
|
||||
int state = 0; // 0: reading input,
|
||||
try(BufferedReader fileReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8))) {
|
||||
String line;
|
||||
while((line = fileReader.readLine()) != null) {
|
||||
// (part of) empty line barrier
|
||||
if(line.isEmpty()) {
|
||||
if(canIncrement) {
|
||||
state++;
|
||||
canIncrement = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
canIncrement = true;
|
||||
// Input line
|
||||
if(state == 0) {
|
||||
result.input.add(line);
|
||||
}
|
||||
// Expected tellraw output line
|
||||
else if(state == 1) {
|
||||
if(expectedConsoleOutputBuilder == null) {
|
||||
expectedConsoleOutputBuilder = new StringBuilder();
|
||||
} else {
|
||||
expectedConsoleOutputBuilder.append("\n");
|
||||
}
|
||||
expectedConsoleOutputBuilder.append(line);
|
||||
}
|
||||
// Expected console output line
|
||||
else if(state == 2) {
|
||||
if(expectedTellrawOutputBuilder == null) {
|
||||
expectedTellrawOutputBuilder = new StringBuilder();
|
||||
} else {
|
||||
expectedTellrawOutputBuilder.append("\n");
|
||||
}
|
||||
expectedTellrawOutputBuilder.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.expectedConsoleOutput = expectedConsoleOutputBuilder == null ? "" : expectedConsoleOutputBuilder.toString();
|
||||
result.expectedTellrawOutput = expectedTellrawOutputBuilder == null ? "" : expectedTellrawOutputBuilder.toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
first line\n
|
||||
second line
|
||||
|
||||
first line
|
||||
second line
|
||||
|
||||
[
|
||||
{
|
||||
"text": "first line"
|
||||
},
|
||||
{
|
||||
"text": "second line"
|
||||
}
|
||||
]
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
first line[break]
|
||||
second line
|
||||
|
||||
first line
|
||||
second line
|
||||
|
||||
[
|
||||
{
|
||||
"text": "first line"
|
||||
},
|
||||
{
|
||||
"text": "second line"
|
||||
}
|
||||
]
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
\&2not black
|
||||
\&5not colored
|
||||
still \&8white
|
||||
|
||||
&2not black&5not coloredstill &8white
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "\u00262not black"
|
||||
},
|
||||
{
|
||||
"text": "\u00265not colored"
|
||||
},
|
||||
{
|
||||
"text": "still \u00268white"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
\&lnot bold
|
||||
\&unot underlined
|
||||
still \&knot crazy
|
||||
|
||||
&lnot bold&unot underlinedstill &knot crazy
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "\u0026lnot bold"
|
||||
},
|
||||
{
|
||||
"text": "\u0026unot underlined"
|
||||
},
|
||||
{
|
||||
"text": "still \u0026knot crazy"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
\[black]not black
|
||||
\[green]not colored
|
||||
still \[blue]white
|
||||
|
||||
[black]not black[green]not coloredstill [blue]white
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "[black]not black"
|
||||
},
|
||||
{
|
||||
"text": "[green]not colored"
|
||||
},
|
||||
{
|
||||
"text": "still [blue]white"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
[bold]bold[/bold], not bold[b], b[/b], not b
|
||||
[strikethrough]strikethrough[/strikethrough], not strikethrough[strike], strike, [/strike], not strike, [s]s, [/s], not s
|
||||
[underline]underline[/underline], not underline[u], u, [/u], not u
|
||||
[italic]italic[/italic], not italic[i], i, [/i], not i
|
||||
[obfuscate]obfuscated[/obfuscate], not obfuscated[o], o, [/o], not o
|
||||
|
||||
§lbold§r, not bold§l, b§r, not b§sstrikethrough§r, not strikethrough§s, strike, §r, not strike, §ss, §r, not s§nunderline§r, not underline§n, u, §r, not u§oitalic§r, not italic§o, i, §r, not i§kobfuscated§r, not obfuscated§k, o, §r, not o
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "bold",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": ", not bold"
|
||||
},
|
||||
{
|
||||
"text": ", b",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": ", not b"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "strikethrough",
|
||||
"strikethrough": true
|
||||
},
|
||||
{
|
||||
"text": ", not strikethrough"
|
||||
},
|
||||
{
|
||||
"text": ", strike, ",
|
||||
"strikethrough": true
|
||||
},
|
||||
{
|
||||
"text": ", not strike, "
|
||||
},
|
||||
{
|
||||
"text": "s, ",
|
||||
"strikethrough": true
|
||||
},
|
||||
{
|
||||
"text": ", not s"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "underline",
|
||||
"underlined": true
|
||||
},
|
||||
{
|
||||
"text": ", not underline"
|
||||
},
|
||||
{
|
||||
"text": ", u, ",
|
||||
"underlined": true
|
||||
},
|
||||
{
|
||||
"text": ", not u"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "italic",
|
||||
"italic": true
|
||||
},
|
||||
{
|
||||
"text": ", not italic"
|
||||
},
|
||||
{
|
||||
"text": ", i, ",
|
||||
"italic": true
|
||||
},
|
||||
{
|
||||
"text": ", not i"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "obfuscated",
|
||||
"obfuscated": true
|
||||
},
|
||||
{
|
||||
"text": ", not obfuscated"
|
||||
},
|
||||
{
|
||||
"text": ", o, ",
|
||||
"obfuscated": true
|
||||
},
|
||||
{
|
||||
"text": ", not o"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
A message that has a fancy hover!
|
||||
hover: [red]~~~[bold]Fancy hover[/bold]~~~
|
||||
|
||||
A message that has a fancy hover!
|
||||
|
||||
[
|
||||
{
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": [
|
||||
{
|
||||
"text": "~~~",
|
||||
"color": "red"
|
||||
},
|
||||
{
|
||||
"text": "Fancy hover",
|
||||
"color": "red",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": "~~~",
|
||||
"color": "red"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
+9
-9
@@ -7,13 +7,13 @@ second line
|
||||
third line
|
||||
|
||||
[
|
||||
{
|
||||
"text": "first line"
|
||||
},
|
||||
{
|
||||
"text": "second line"
|
||||
},
|
||||
{
|
||||
"text": "third line"
|
||||
}
|
||||
[
|
||||
"first line"
|
||||
],
|
||||
[
|
||||
"second line"
|
||||
],
|
||||
[
|
||||
"third line"
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
first line\n
|
||||
second line
|
||||
|
||||
first line
|
||||
second line
|
||||
|
||||
[
|
||||
[
|
||||
"first line"
|
||||
],
|
||||
[
|
||||
"second line"
|
||||
]
|
||||
]
|
||||
+9
-9
@@ -7,13 +7,13 @@ second line
|
||||
third line
|
||||
|
||||
[
|
||||
{
|
||||
"text": "first line"
|
||||
},
|
||||
{
|
||||
"text": "second line"
|
||||
},
|
||||
{
|
||||
"text": "third line"
|
||||
}
|
||||
[
|
||||
"first line"
|
||||
],
|
||||
[
|
||||
"second line"
|
||||
],
|
||||
[
|
||||
"third line"
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
first line[break]
|
||||
second line
|
||||
|
||||
first line
|
||||
second line
|
||||
|
||||
[
|
||||
[
|
||||
"first line"
|
||||
],
|
||||
[
|
||||
"second line"
|
||||
]
|
||||
]
|
||||
+1
-3
@@ -81,9 +81,7 @@
|
||||
"text": "yellow,",
|
||||
"color": "yellow"
|
||||
},
|
||||
{
|
||||
"text": "white,"
|
||||
}
|
||||
"white,"
|
||||
]
|
||||
}
|
||||
]
|
||||
+1
-3
@@ -81,9 +81,7 @@
|
||||
"text": "yellow,",
|
||||
"color": "yellow"
|
||||
},
|
||||
{
|
||||
"text": "white,"
|
||||
}
|
||||
"white,"
|
||||
]
|
||||
}
|
||||
]
|
||||
+1
-3
@@ -6,9 +6,7 @@ Message with §aa part in green and §ca part in red
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Message with "
|
||||
},
|
||||
"Message with ",
|
||||
{
|
||||
"text": "a part in green and ",
|
||||
"color": "green"
|
||||
File renamed without changes.
+1
-3
@@ -6,9 +6,7 @@ Message with §aa part in green
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Message with "
|
||||
},
|
||||
"Message with ",
|
||||
{
|
||||
"text": "a part in green",
|
||||
"color": "green"
|
||||
+1
-3
@@ -91,9 +91,7 @@
|
||||
"text": "yellow,",
|
||||
"color": "yellow"
|
||||
},
|
||||
{
|
||||
"text": "white"
|
||||
},
|
||||
"white",
|
||||
{
|
||||
"text": "darkblue,",
|
||||
"color": "dark_blue"
|
||||
+2
-6
@@ -14,16 +14,12 @@
|
||||
"text": "a part in green ",
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"text": " white "
|
||||
},
|
||||
" white ",
|
||||
{
|
||||
"text": "green, ",
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"text": "white, "
|
||||
},
|
||||
"white, ",
|
||||
{
|
||||
"text": "last green",
|
||||
"color": "green"
|
||||
+1
-3
@@ -6,9 +6,7 @@ Message with §aa part in green and §ca part in red
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Message with "
|
||||
},
|
||||
"Message with ",
|
||||
{
|
||||
"text": "a part in green and ",
|
||||
"color": "green"
|
||||
File renamed without changes.
+1
-3
@@ -6,9 +6,7 @@ Message with §aa part in green
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Message with "
|
||||
},
|
||||
"Message with ",
|
||||
{
|
||||
"text": "a part in green",
|
||||
"color": "green"
|
||||
@@ -0,0 +1,34 @@
|
||||
é\n
|
||||
à\n
|
||||
ê\n
|
||||
╔═╗ ┌─┐\n
|
||||
║ ║ │ │\n
|
||||
╚═╝ └─┘
|
||||
|
||||
é
|
||||
à
|
||||
ê
|
||||
╔═╗ ┌─┐
|
||||
║ ║ │ │
|
||||
╚═╝ └─┘
|
||||
|
||||
[
|
||||
[
|
||||
"é"
|
||||
],
|
||||
[
|
||||
"à"
|
||||
],
|
||||
[
|
||||
"ê"
|
||||
],
|
||||
[
|
||||
"╔═╗ ┌─┐"
|
||||
],
|
||||
[
|
||||
"║ ║ │ │"
|
||||
],
|
||||
[
|
||||
"╚═╝ └─┘"
|
||||
]
|
||||
]
|
||||
+3
-9
@@ -8,15 +8,11 @@ Text with backslashes, one: \, two: \\, two as well (can't happen for user input
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Text with backslashes, one: \\, two: \\\\, two as well (can\u0027t happen for user input): \\\\"
|
||||
},
|
||||
"Text with backslashes, one: \\, two: \\\\, two as well (can\u0027t happen for user input): \\\\",
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Not escaped color: \\"
|
||||
},
|
||||
"Not escaped color: \\",
|
||||
{
|
||||
"text": "is red, \\",
|
||||
"color": "red"
|
||||
@@ -31,9 +27,7 @@ Text with backslashes, one: \, two: \\, two as well (can't happen for user input
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "Is escaped: \\[red]still white, \\\\"
|
||||
},
|
||||
"Is escaped: \\[red]still white, \\\\",
|
||||
{
|
||||
"text": "actually blue, \\\\[green]still blue",
|
||||
"color": "blue"
|
||||
+3
-3
@@ -3,7 +3,7 @@ message without \[break], yes \\nreally!
|
||||
message without [break], yes \nreally!
|
||||
|
||||
[
|
||||
{
|
||||
"text": "message without [break], yes \\nreally!"
|
||||
}
|
||||
[
|
||||
"message without [break], yes \\nreally!"
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
\&2not black
|
||||
\&5not colored
|
||||
still \&8white
|
||||
|
||||
&2not black&5not coloredstill &8white
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
"\u00262not black",
|
||||
"\u00265not colored",
|
||||
"still \u00268white"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
\&lnot bold
|
||||
\&unot underlined
|
||||
still \&knot crazy
|
||||
|
||||
&lnot bold&unot underlinedstill &knot crazy
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
"\u0026lnot bold",
|
||||
"\u0026unot underlined",
|
||||
"still \u0026knot crazy"
|
||||
]
|
||||
}
|
||||
]
|
||||
File renamed without changes.
@@ -0,0 +1,16 @@
|
||||
\[black]not black
|
||||
\[green]not colored
|
||||
still \[blue]white
|
||||
|
||||
[black]not black[green]not coloredstill [blue]white
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
"[black]not black",
|
||||
"[green]not colored",
|
||||
"still [blue]white"
|
||||
]
|
||||
}
|
||||
]
|
||||
+4
-10
@@ -8,15 +8,9 @@ still \[obfuscate]whi\[o]te
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "[bold]not [b]bold"
|
||||
},
|
||||
{
|
||||
"text": "[underline]not [u]colored"
|
||||
},
|
||||
{
|
||||
"text": "still [obfuscate]whi[o]te"
|
||||
}
|
||||
"[bold]not [b]bold",
|
||||
"[underline]not [u]colored",
|
||||
"still [obfuscate]whi[o]te"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
File renamed without changes.
File renamed without changes.
+4
-6
@@ -8,16 +8,14 @@ message §9in blue
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "message "
|
||||
},
|
||||
"message ",
|
||||
{
|
||||
"text": "in blue",
|
||||
"color": "blue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "simple second line"
|
||||
}
|
||||
[
|
||||
"simple second line"
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,136 @@
|
||||
[bold]bold[/bold], not bold\n
|
||||
[b], b[/b], not b\n
|
||||
[strikethrough]strikethrough[/strikethrough], not strikethrough\n
|
||||
[strike], strike, [/strike], not strike\n
|
||||
[s]s, [/s], not s\n
|
||||
[underline]underline[/underline], not underline\n
|
||||
[u], u, [/u], not u\n
|
||||
[italic]italic[/italic], not italic\n
|
||||
[i], i, [/i], not i\n
|
||||
[obfuscate]obfuscated[/obfuscate], not obfuscated\n
|
||||
[o], o, [/o], not o
|
||||
|
||||
§lbold§r, not bold
|
||||
§l, b§r, not b
|
||||
§sstrikethrough§r, not strikethrough
|
||||
§s, strike, §r, not strike
|
||||
§ss, §r, not s
|
||||
§nunderline§r, not underline
|
||||
§n, u, §r, not u
|
||||
§oitalic§r, not italic
|
||||
§o, i, §r, not i
|
||||
§kobfuscated§r, not obfuscated
|
||||
§k, o, §r, not o
|
||||
|
||||
[
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "bold",
|
||||
"bold": true
|
||||
},
|
||||
", not bold"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": ", b",
|
||||
"bold": true
|
||||
},
|
||||
", not b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "strikethrough",
|
||||
"strikethrough": true
|
||||
},
|
||||
", not strikethrough"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": ", strike, ",
|
||||
"strikethrough": true
|
||||
},
|
||||
", not strike"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "s, ",
|
||||
"strikethrough": true
|
||||
},
|
||||
", not s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "underline",
|
||||
"underlined": true
|
||||
},
|
||||
", not underline"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": ", u, ",
|
||||
"underlined": true
|
||||
},
|
||||
", not u"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "italic",
|
||||
"italic": true
|
||||
},
|
||||
", not italic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": ", i, ",
|
||||
"italic": true
|
||||
},
|
||||
", not i"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "obfuscated",
|
||||
"obfuscated": true
|
||||
},
|
||||
", not obfuscated"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": ", o, ",
|
||||
"obfuscated": true
|
||||
},
|
||||
", not o"
|
||||
]
|
||||
}
|
||||
]
|
||||
+2
-6
@@ -14,16 +14,12 @@
|
||||
"text": "a part in bold ",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": " not in bold "
|
||||
},
|
||||
" not in bold ",
|
||||
{
|
||||
"text": "bold, ",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": "not bold"
|
||||
}
|
||||
"not bold"
|
||||
]
|
||||
},
|
||||
{
|
||||
+4
-12
@@ -7,29 +7,21 @@ A §amessage§f that has a §lfancy§r hover!
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "A "
|
||||
},
|
||||
"A ",
|
||||
{
|
||||
"text": "message",
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"text": " that has a "
|
||||
},
|
||||
" that has a ",
|
||||
{
|
||||
"text": "fancy",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": " hover!"
|
||||
}
|
||||
" hover!"
|
||||
],
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "~~~Fancy hover~~~"
|
||||
}
|
||||
"value": "~~~Fancy hover~~~"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
A message that has a fancy hover!
|
||||
hover: [red]~~~[bold]Fancy hover[/bold]~~~
|
||||
|
||||
A message that has a fancy hover!
|
||||
|
||||
[
|
||||
{
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "~~~",
|
||||
"color": "red"
|
||||
},
|
||||
{
|
||||
"text": "Fancy hover",
|
||||
"color": "red",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": "~~~",
|
||||
"color": "red"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
+6
-18
@@ -21,54 +21,42 @@ one, two, three, four, five, six
|
||||
"text": "one",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "one"
|
||||
}
|
||||
"value": "one"
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": ", two",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "two"
|
||||
}
|
||||
"value": "two"
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": ", three",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "three"
|
||||
}
|
||||
"value": "three"
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": ", four",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "four"
|
||||
}
|
||||
"value": "four"
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": ", five",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "five"
|
||||
}
|
||||
"value": "five"
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": ", six",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "six"
|
||||
}
|
||||
"value": "six"
|
||||
}
|
||||
}
|
||||
]
|
||||
+2
-6
@@ -13,18 +13,14 @@ A message that has a fancy hover! more text
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "~~~Fancy hover~~~"
|
||||
}
|
||||
"value": "~~~Fancy hover~~~"
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": " more text",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "Also an hover!"
|
||||
}
|
||||
"value": "Also an hover!"
|
||||
}
|
||||
}
|
||||
]
|
||||
+8
-11
@@ -13,19 +13,16 @@ A message that has a fancy hover! more text
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": [
|
||||
{
|
||||
"text": "~~~Fancy hover~~~\n"
|
||||
},
|
||||
{
|
||||
"text": "Another line!"
|
||||
}
|
||||
]
|
||||
"value": {
|
||||
"text": "",
|
||||
"extra": [
|
||||
"~~~Fancy hover~~~\n",
|
||||
"Another line!"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": " more text"
|
||||
}
|
||||
" more text"
|
||||
]
|
||||
}
|
||||
]
|
||||
+8
-11
@@ -12,19 +12,16 @@ A message that has a fancy hover! more text
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": [
|
||||
{
|
||||
"text": "~~~Fancy hover~~~\n"
|
||||
},
|
||||
{
|
||||
"text": "Another line!"
|
||||
}
|
||||
]
|
||||
"value": {
|
||||
"text": "",
|
||||
"extra": [
|
||||
"~~~Fancy hover~~~\n",
|
||||
"Another line!"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": " more text"
|
||||
}
|
||||
" more text"
|
||||
]
|
||||
}
|
||||
]
|
||||
+8
-11
@@ -12,19 +12,16 @@ A message that has a fancy hover! more text
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": [
|
||||
{
|
||||
"text": "~~~Fancy hover~~~\n"
|
||||
},
|
||||
{
|
||||
"text": "Another line!"
|
||||
}
|
||||
]
|
||||
"value": {
|
||||
"text": "",
|
||||
"extra": [
|
||||
"~~~Fancy hover~~~\n",
|
||||
"Another line!"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": " more text"
|
||||
}
|
||||
" more text"
|
||||
]
|
||||
}
|
||||
]
|
||||
+3
-3
@@ -3,7 +3,7 @@ To add tooltips use this: `hover: ~~~Fancy hover~~~`
|
||||
To add tooltips use this: `hover: ~~~Fancy hover~~~`
|
||||
|
||||
[
|
||||
{
|
||||
"text": "To add tooltips use this: `hover: ~~~Fancy hover~~~`"
|
||||
}
|
||||
[
|
||||
"To add tooltips use this: `hover: ~~~Fancy hover~~~`"
|
||||
]
|
||||
]
|
||||
+1
-3
@@ -8,9 +8,7 @@ A message that has a fancy hover!
|
||||
"text": "A message that has a fancy hover!",
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"value": {
|
||||
"text": "~~~Fancy hover~~~"
|
||||
}
|
||||
"value": "~~~Fancy hover~~~"
|
||||
}
|
||||
}
|
||||
]
|
||||
+5
-15
@@ -9,9 +9,7 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "clean"
|
||||
},
|
||||
"clean",
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
@@ -24,9 +22,7 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
"color": "red",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": "clean"
|
||||
}
|
||||
"clean"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,24 +46,18 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
"color": "dark_blue",
|
||||
"underlined": true
|
||||
},
|
||||
{
|
||||
"text": "clean"
|
||||
}
|
||||
"clean"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "clean, "
|
||||
},
|
||||
"clean, ",
|
||||
{
|
||||
"text": "red, ",
|
||||
"color": "red"
|
||||
},
|
||||
{
|
||||
"text": "still clean"
|
||||
}
|
||||
"still clean"
|
||||
]
|
||||
}
|
||||
]
|
||||
+5
-15
@@ -9,9 +9,7 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "clean"
|
||||
},
|
||||
"clean",
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
@@ -24,9 +22,7 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
"color": "red",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": "clean"
|
||||
}
|
||||
"clean"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,24 +46,18 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
"color": "dark_blue",
|
||||
"underlined": true
|
||||
},
|
||||
{
|
||||
"text": "clean"
|
||||
}
|
||||
"clean"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "clean, "
|
||||
},
|
||||
"clean, ",
|
||||
{
|
||||
"text": "red, ",
|
||||
"color": "red"
|
||||
},
|
||||
{
|
||||
"text": "still clean"
|
||||
}
|
||||
"still clean"
|
||||
]
|
||||
}
|
||||
]
|
||||
+5
-15
@@ -9,9 +9,7 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "clean"
|
||||
},
|
||||
"clean",
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
@@ -24,9 +22,7 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
"color": "red",
|
||||
"bold": true
|
||||
},
|
||||
{
|
||||
"text": "clean"
|
||||
}
|
||||
"clean"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,24 +46,18 @@ clean§cred, §lred+bold, §rclean§9blue, §agreen, §ngreen+underline, §1dark
|
||||
"color": "dark_blue",
|
||||
"underlined": true
|
||||
},
|
||||
{
|
||||
"text": "clean"
|
||||
}
|
||||
"clean"
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "clean, "
|
||||
},
|
||||
"clean, ",
|
||||
{
|
||||
"text": "red, ",
|
||||
"color": "red"
|
||||
},
|
||||
{
|
||||
"text": "still clean"
|
||||
}
|
||||
"still clean"
|
||||
]
|
||||
}
|
||||
]
|
||||
+3
-3
@@ -3,7 +3,7 @@ A simple message
|
||||
A simple message
|
||||
|
||||
[
|
||||
{
|
||||
"text": "A simple message"
|
||||
}
|
||||
[
|
||||
"A simple message"
|
||||
]
|
||||
]
|
||||
+2
-6
@@ -7,12 +7,8 @@ A simple messagesplit into two parts
|
||||
{
|
||||
"text": "",
|
||||
"extra": [
|
||||
{
|
||||
"text": "A simple message"
|
||||
},
|
||||
{
|
||||
"text": "split into two parts"
|
||||
}
|
||||
"A simple message",
|
||||
"split into two parts"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
name: InteractiveMessengerTest
|
||||
main: me.wiefferink.interactivemessenger.testing.ingame.IngameTest
|
||||
version: "${project.version}"
|
||||
description: "Ingame testing of the InteractiveMessenger project."
|
||||
author: NLThijs48 and Phoenix
|
||||
permissions:
|
||||
interactivemessenger.test:
|
||||
description: Use the /testInteractiveMessenger command to test it ingame
|
||||
default: op
|
||||
commands:
|
||||
testInteractiveMessenger:
|
||||
usage: /testinteractivemessenger
|
||||
description: Test the interactive messenger library
|
||||
aliases: [tim]
|
||||
Reference in new issue
Block a user