🦔 SonicTab - Create a custom TabList faster than Sonic.
10 | SonicTab is an API (Application Programming Interface) that will allow you to create customized TabList(s) easily and fastly. By offering you a simple to understand 2-Dimensional Grid system.
11 |
12 |
13 |
14 |
15 |
16 | ## 🏷️ Features
17 |
18 | - 2D Grid system
19 | - Multi-Line header and footer
20 | - Fullscreen mode
21 | - Cross platform
22 |
23 |
24 | ## 📂 Installation
25 |
26 | Installation of SonicTab
27 |
28 | ```bash
29 | git clone https://github.com/rootxls/SonicTab
30 | ```
31 |
32 | Now you can open this folder with your IDE.
33 |
34 | ## 📐 Usage
35 |
36 |
37 | **Template**
38 |
39 | First, you'll have to create a new TabList template, this will contain all your TabList properties.
40 | ```java
41 | private final TabListTemplate template = new TabListTemplate();
42 | ```
43 |
44 | **Header / Footer**
45 |
46 | Secondly, you can add Header & Footer, thoses features allows you to put text on top and bottom of the tablist.
47 |
48 | ```java
49 | // HEADER
50 | template.getHeader().addLines("Line 1", "Line 2");
51 |
52 | // FOOTER
53 | template.getFooter().addLines("Line 1", "Line 2");
54 | ```
55 |
56 | **Body Customization**
57 |
58 | Afterwards, you can add a Body. Originally this area contains the list of players. But with SonicTab you can modify it to add whatever you want. To get this part of your template, use:
59 |
60 | ```java
61 | template.getBody();
62 | ```
63 |
64 | | Variable name | Min. value | Max. value |
65 | |---|---|---|
66 | | Columns | 1 | 4 |
67 | | X | 0 | 3 |
68 | | Y | 0 | 19 |
69 | | lineWidth | 0 | 48 |
70 |
71 | Such as:
72 | Columns: Define the number of columns you want for your tablist:
73 |
74 | ```java
75 | template.getBody().setColumns(3);
76 | ```
77 |
78 | RemoveBaseLines: Removing base players:
79 |
80 | ```java
81 | template.getBody().setRemoveBaseLines(true);
82 | ```
83 | Custom Lines: Add or delete your own lines into the tablist body:
84 |
85 | ```java
86 | // ADD
87 | int ping = 0;
88 | int x, z = 0;
89 | template.getBody().addLine(new BodyLine("YOUR LINE", ping, x, z));
90 |
91 | // REMOVE
92 | template.getBody().removeLine(x, z); // THE LINE AT COORDINATES X & Z WILL BE DELETED AND REPLACED BY A BLANK LINE
93 | ```
94 |
95 | With lines you can modify displayed head too:
96 |
97 | ```java
98 | BodyLine line = new BodyLine("YOUR LINE", ping, x, z);
99 |
100 | // TEXTURE & SIGNATURE CAN BE OPTAINED BY UPLOADING SKIN ON https://mineskin.org/
101 | String texture = "";
102 | String signature = "";
103 |
104 | line.setSkin(texture, signature);
105 | ```
106 |
107 | ## ❓ FAQ
108 |
109 | #### From which version can be used this API ?
110 |
111 | From 1.8 to 1.17
112 |
113 |
114 | ## 🤝 Contributing
115 |
116 | Contributions are always welcome!
117 |
118 | See `contributing.md` for ways to get started.
119 |
120 | Please adhere to this project's `code of conduct`.
121 |
122 |
123 | ## 📎 Related
124 |
125 | Here are the projects for which this API has been developed:
126 |
127 | - [LyraMC](https://github.com/LyraMC)
128 |
129 |
130 | ## 🔗 Links
131 | [](https://bsnk.tk/)
132 | [](https://www.linkedin.com/in/bastien-siniak/)
133 |
134 |
135 |
--------------------------------------------------------------------------------
/src/main/java/dev/fls/tablist/animations/tasks/TablistAnimationTask.java:
--------------------------------------------------------------------------------
1 | package dev.fls.tablist.animations.tasks;
2 |
3 | import dev.fls.tablist.animations.TablistAnimation;
4 | import dev.fls.tablist.page.OptionalPart;
5 | import org.bukkit.Bukkit;
6 | import org.bukkit.scheduler.BukkitRunnable;
7 |
8 | public class TablistAnimationTask extends BukkitRunnable {
9 |
10 | private final TablistAnimation animation;
11 |
12 | public TablistAnimationTask(TablistAnimation animation) {
13 | this.animation = animation;
14 | }
15 |
16 | private int currentHeaderPause, currentFooterPause, currentBodyPause;
17 | private int currentHeaderDelay, currentFooterDelay, currentBodyDelay;
18 | private int currentHeader, currentFooter, currentBody;
19 |
20 | @Override
21 | public void run() {
22 | animation.setRunning(true);
23 |
24 | // TODO actualiser qu'une fois header et footer ou bien actualiser indépendement header et footer
25 |
26 | if (currentHeaderDelay != -1) currentHeaderDelay++;
27 | if (currentBodyDelay != -1) currentBodyDelay++;
28 | if (currentFooterDelay != -1) currentFooterDelay++;
29 |
30 | header: {
31 | if (animation.isAnimateHeader()) {
32 | if (currentHeaderDelay == animation.getHeaderInterval()) {
33 | if(currentHeader < 0) break header;
34 | animation.getTemplate().getHeader().setLines(animation.getHeaderFrames().get(currentHeader));
35 | Bukkit.getOnlinePlayers().forEach(player -> OptionalPart.sendPacket(player, animation.getTemplate().getHeader(), animation.getTemplate().getFooter()));
36 | currentHeader = applyCurrent(currentHeader, animation.getHeaderFrames().size());
37 | currentHeaderDelay = 0;
38 | } else {
39 | if(currentHeader == -1) {
40 | if(currentHeaderPause >= animation.getHeaderAnimationDelay()) {
41 | currentHeaderPause = 0;
42 | currentHeaderDelay = 0;
43 | currentHeader = 0;
44 | } else {
45 | currentHeaderPause++;
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | body: {
53 | if (animation.isAnimateBody()) {
54 | if (currentBodyDelay == animation.getBodyInterval()) {
55 | if(currentBody < 0) break body;
56 | animation.getTemplate().setBody(animation.getBodyFrames().get(currentBody));
57 | Bukkit.getOnlinePlayers().forEach(player -> animation.getTemplate().getBody().show(player));
58 | currentBody = applyCurrent(currentBody, animation.getBodyFrames().size());
59 | currentBodyDelay = 0;
60 | } else {
61 | if(currentBody == -1) {
62 | if(currentBodyPause >= animation.getBodyAnimationDelay()) {
63 | currentBodyPause = 0;
64 | currentBodyDelay = 0;
65 | currentBody = 0;
66 | } else {
67 | currentBodyPause++;
68 | }
69 | }
70 | }
71 | }
72 | }
73 |
74 | footer: {
75 | if (animation.isAnimateFooter()) {
76 | if (currentFooterDelay == animation.getFooterInterval()) {
77 | if(currentFooter < 0) break footer;
78 | animation.getTemplate().getFooter().setLines(animation.getFooterFrames().get(currentFooter));
79 | Bukkit.getOnlinePlayers().forEach(player -> OptionalPart.sendPacket(player, animation.getTemplate().getHeader(), animation.getTemplate().getFooter()));
80 | currentFooter = applyCurrent(currentFooter, animation.getFooterFrames().size());
81 | currentFooterDelay = 0;
82 | } else {
83 | if(currentFooter == -1) {
84 | if(currentFooterPause >= animation.getBodyAnimationDelay()) {
85 | currentFooterPause = 0;
86 | currentFooterDelay = 0;
87 | currentFooter = 0;
88 | } else {
89 | currentFooterPause++;
90 | }
91 | }
92 | }
93 | }
94 | }
95 | }
96 |
97 | @Override
98 | public synchronized void cancel() throws IllegalStateException {
99 | animation.setRunning(false);
100 | super.cancel();
101 | }
102 |
103 | private int applyCurrent(int current, int frames) {
104 | if (current < frames - 1) {
105 | current++;
106 | } else {
107 | current = -1;
108 | }
109 |
110 | return current;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/dev/fls/tablist/page/parts/body/lines/BodyLine.java:
--------------------------------------------------------------------------------
1 | package dev.fls.tablist.page.parts.body.lines;
2 |
3 | import com.google.gson.Gson;
4 | import com.mojang.authlib.GameProfile;
5 | import com.mojang.authlib.properties.Property;
6 | import com.mojang.util.UUIDTypeAdapter;
7 | import dev.fls.tablist.utils.PacketUtils;
8 | import dev.fls.tablist.utils.mojangapi.MinecraftProfile;
9 | import net.minecraft.server.v1_8_R3.*;
10 | import org.bukkit.Bukkit;
11 | import org.bukkit.craftbukkit.v1_8_R3.CraftServer;
12 | import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
13 | import org.bukkit.entity.Player;
14 |
15 | import javax.net.ssl.HttpsURLConnection;
16 | import java.io.BufferedReader;
17 | import java.io.IOException;
18 | import java.io.InputStreamReader;
19 | import java.lang.reflect.Field;
20 | import java.net.URL;
21 | import java.util.List;
22 | import java.util.UUID;
23 | import java.util.stream.Collectors;
24 | import java.util.stream.Stream;
25 |
26 | public class BodyLine {
27 |
28 | private LineType lineType;
29 | private final EntityPlayer entityPlayer;
30 | private int ping;
31 | private String text, correctLengthText;
32 | private final int x,z;
33 |
34 | public BodyLine(String text, int ping, int x, int z) {
35 | this(text, ping, x, z, x + "." + getZlineCode(z), null);
36 | lineType = LineType.BLANK;
37 | }
38 |
39 | public BodyLine(String text, int ping, int x, int z, EntityPlayer entityPlayer) {
40 | this(text, ping, x, z, x + "." + getZlineCode(z), entityPlayer);
41 | lineType = LineType.PLAYER;
42 | }
43 |
44 | public BodyLine(String text, int ping, int x, int z, String name) {
45 | this(text, ping, x, z, name, null);
46 | lineType = LineType.BLANK;
47 | }
48 |
49 | public BodyLine(String text, int x, int z, String name, EntityPlayer entityPlayer) {
50 | this(text, entityPlayer.ping, x, z, name, entityPlayer);
51 | lineType = LineType.PLAYER;
52 | }
53 |
54 | public BodyLine(String text, int ping, int x, int z, String name, EntityPlayer entityPlayer) {
55 | MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
56 | WorldServer world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
57 | if(entityPlayer != null) {
58 | this.entityPlayer = entityPlayer;
59 | this.ping = entityPlayer.ping;
60 | lineType = LineType.PLAYER;
61 | } else {
62 | this.entityPlayer = new EntityPlayer(nmsServer, world, new GameProfile(UUID.randomUUID(), name), new PlayerInteractManager(world));
63 | this.entityPlayer.ping = this.ping;
64 | this.ping = ping;
65 | }
66 | this.text = text;
67 | this.correctLengthText = text;
68 | this.x = x;
69 | this.z = z;
70 |
71 | setName();
72 | }
73 |
74 | /**
75 | * TODO find better method
76 | * @param z
77 | * @return
78 | */
79 | private static String getZlineCode(int z) {
80 | String strz = z + "";
81 |
82 | if (z < 10 && z > 0) {
83 | return 10 + strz;
84 | } else {
85 | switch (z) {
86 | case 10:
87 | return 110 + "";
88 | case 11:
89 | return 111 + "";
90 | default:
91 | break;
92 | }
93 | }
94 |
95 | return strz;
96 | }
97 |
98 | private void setName() {
99 | try {
100 | Field headerField = entityPlayer.getClass().getDeclaredField("listName");
101 | headerField.setAccessible(true);
102 | headerField.set(entityPlayer, IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + correctLengthText + " \"}"));
103 | headerField.setAccessible(false);
104 | } catch (Exception e) {
105 | e.printStackTrace();
106 | }
107 | }
108 |
109 | public BodyLine setSkin(String value, String signature) {
110 | entityPlayer.getProfile().getProperties().removeAll("textures");
111 | entityPlayer.getProfile().getProperties().put("textures", new Property("textures", value, signature));
112 | return this;
113 | }
114 |
115 | public BodyLine setSkin(UUID uuid) {
116 | try {
117 | HttpsURLConnection connection = (HttpsURLConnection) new URL(String.format("https://sessionserver.mojang.com/session/minecraft/profile/%s?unsigned=false", UUIDTypeAdapter.fromUUID(uuid))).openConnection();
118 | if (connection.getResponseCode() == HttpsURLConnection.HTTP_OK) {
119 | Stream reply = new BufferedReader(new InputStreamReader(connection.getInputStream())).lines();
120 | List lines = reply.collect(Collectors.toList());
121 | String line = "";
122 | for(String str : lines) {
123 | line += str;
124 | }
125 | Gson gson = new Gson();
126 | MinecraftProfile profile = gson.fromJson(line, MinecraftProfile.class);
127 | entityPlayer.getProfile().getProperties().put("textures", new Property("textures", profile.getProperties()[0].getValue(), profile.getProperties()[0].getSignature()));
128 | } else {
129 | Bukkit.getLogger().severe("BodyLine("+ x +"."+ z + ") skin could not be loaded. Reason: " + connection.getResponseCode() + ", " + connection.getResponseMessage());
130 | }
131 | } catch (IOException e) {
132 | e.printStackTrace();
133 | }
134 | return this;
135 | }
136 |
137 | public void setPing(int ping) {
138 | this.ping = ping;
139 | entityPlayer.ping = this.ping;
140 | }
141 |
142 | public void setText(String text) {
143 | if(text.length() > 48) text = text.substring(0,47);
144 | this.text = text;
145 | setName();
146 | }
147 |
148 | public LineType getLineType() {
149 | return lineType;
150 | }
151 |
152 | public String getText() {
153 | return text;
154 | }
155 |
156 | public int getX() {
157 | return x;
158 | }
159 |
160 | public int getZ() {
161 | return z;
162 | }
163 |
164 | public String getCorrectLengthText() {
165 | return correctLengthText;
166 | }
167 |
168 | public void setCorrectLengthText(String txt) {
169 | this.correctLengthText = txt;
170 | setName();
171 | }
172 |
173 | public void show(Player player) {
174 | Packet[] packets = new Packet[]{
175 | new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entityPlayer),
176 | new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, entityPlayer),
177 | };
178 |
179 | for(Packet packet : packets) {
180 | PacketUtils.sendPacket(player, packet);
181 | }
182 | }
183 |
184 | public void updatePing(Player player) {
185 | Packet[] packets = new Packet[]{
186 | new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, entityPlayer),
187 | };
188 |
189 | for(Packet packet : packets) {
190 | PacketUtils.sendPacket(player, packet);
191 | }
192 | }
193 |
194 | public void hide(Player player) {
195 | Packet[] packets = new Packet[]{
196 | new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityPlayer),
197 | };
198 |
199 | for(Packet packet : packets) {
200 | PacketUtils.sendPacket(player, packet);
201 | }
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/src/main/java/dev/fls/tablist/page/parts/body/Body.java:
--------------------------------------------------------------------------------
1 | package dev.fls.tablist.page.parts.body;
2 |
3 | import dev.fls.tablist.TabListTemplate;
4 | import dev.fls.tablist.page.PagePart;
5 | import dev.fls.tablist.page.PartType;
6 | import dev.fls.tablist.page.parts.body.lines.BodyLine;
7 | import dev.fls.tablist.skin.SkinColor;
8 | import dev.fls.tablist.utils.PacketUtils;
9 | import lombok.Getter;
10 | import net.minecraft.server.v1_8_R3.PacketPlayOutPlayerInfo;
11 | import org.bukkit.Bukkit;
12 | import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
13 | import org.bukkit.entity.Player;
14 |
15 | import java.util.*;
16 |
17 | @Getter
18 | public class Body extends PagePart {
19 |
20 | public static final int MAX_LINES = 80;
21 | public static final int MAX_LINES_PER_COLUMN = 20;
22 | public static final int MAX_COLUMNS = MAX_LINES / MAX_LINES_PER_COLUMN;
23 |
24 | private final BodyLine[][] lines = new BodyLine[20][4];
25 | private int columns;
26 | private int lineWidth = 10;
27 | private boolean removeBaseLines = true;
28 |
29 | public Body() {
30 | super(PartType.BODY);
31 | }
32 |
33 | public Body addLine(BodyLine line) {
34 | if(line.getText().length() > lineWidth && lineWidth > -1) line.setText(line.getText().substring(0, lineWidth - 1));
35 | removeLine(line.getX(), line.getZ());
36 | lines[line.getZ()][line.getX()] = line;
37 | return this;
38 | }
39 |
40 | public Body addLines(BodyLine... line) {
41 | for(BodyLine l : line) {
42 | addLine(l);
43 | }
44 | return this;
45 | }
46 |
47 | public Body removeLine(int x, int z) {
48 | BodyLine line = lines[z][x];
49 | if(line != null) {
50 | // TODO faire if tablist affiché alors hide
51 | Bukkit.getOnlinePlayers().forEach(player -> line.hide(player));
52 | addFakeLine(x, z, applyLineWidth(lineWidth));
53 | }
54 | return this;
55 | }
56 |
57 | public Body removeLines(List index) {
58 | for(int x = 0; x < index.size(); x++) {
59 | for(int z = 0; z < index.get(x).length; z++) {
60 | Bukkit.broadcastMessage("DEBUG: removeLine " + x + " " + z);
61 | }
62 | }
63 | return this;
64 | }
65 |
66 | private Body removeBaseLines(Player player) {
67 | for(Player online : Bukkit.getOnlinePlayers()) {
68 | EntityPlayer entityPlayer = ((CraftPlayer) online).getHandle();
69 | List skin = new ArrayList<>(entityPlayer.getProfile().getProperties().get("textures"));
70 |
71 | PacketUtils.sendPacket(player, new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, ((CraftPlayer) online).getHandle()));
72 |
73 | entityPlayer.getProfile().getProperties().removeAll("textures");
74 | entityPlayer.getProfile().getProperties().put("textures", new Property("textures", skin.get(0).getValue(), skin.get(0).getSignature()));
75 |
76 | // a debug
77 | /*if(online == player) continue;
78 | PacketUtils.sendPacket(player, new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entityPlayer));
79 | PacketUtils.sendPacket(player, new PacketPlayOutSpawnEntityLiving(entityPlayer));
80 | PacketUtils.sendPacket(player, new PacketPlayOutNamedEntitySpawn(entityPlayer));*/
81 | }
82 | return this;
83 | }
84 |
85 | public Body setRemoveBaseLines(boolean removeBaseLines) {
86 | this.removeBaseLines = removeBaseLines;
87 | return this;
88 | }
89 |
90 | private String applyLineWidth(int lineWidth) {
91 | String text = "";
92 |
93 | for(int i = 1; i <= lineWidth; i++) {
94 | text += " ";
95 | }
96 | return text;
97 | }
98 |
99 | public Body setColumns(int columns) {
100 | boolean debug = false;
101 |
102 | if(columns > MAX_COLUMNS) columns = MAX_COLUMNS;
103 | this.columns = columns;
104 |
105 | String text = applyLineWidth(lineWidth);
106 |
107 | for(int x = 0; x < lines.length; x++) {
108 | for(int z = 0; z < columns; z++) {
109 | BodyLine actual = lines[x][z];
110 | if(actual == null) {
111 | addFakeLine(z, x, debug ? x + "." + z : text);
112 | }
113 | }
114 | }
115 | return this;
116 | }
117 |
118 | private BodyLine addFakeLine(int x, int z, String txt) {
119 | boolean debug = false;
120 | String text = debug ? x + "." + z + txt : txt;
121 |
122 | SkinColor skinColor = SkinColor.DARK_GRAY;
123 | BodyLine line = new BodyLine(text, 0, x, z)
124 | .setSkin(skinColor.getTexture(), skinColor.getSignature());
125 |
126 | lines[z][x] = line;
127 |
128 | return line;
129 | }
130 |
131 | public int getLineWidth() {
132 | return lineWidth;
133 | }
134 |
135 | public void setLineWidth(int lineWidth) {
136 | this.lineWidth = lineWidth;
137 | int maxLength = 0;
138 | for(int x = 0; x < lines.length; x++) {
139 | for(int z = 0; z < lines[x].length; z++) {
140 | BodyLine line = lines[x][z];
141 | if(line == null) continue;
142 |
143 | int index = line.getText().length() < lineWidth ? line.getText().length() : lineWidth - 1;
144 | if (index + 1 > lineWidth) index = lineWidth;
145 |
146 | String text = line.getText().substring(0, index);
147 | if (maxLength < text.length()) maxLength = text.length();
148 |
149 | line.setCorrectLengthText(text);
150 | }
151 | }
152 | }
153 |
154 | private int maxLines() {
155 | return columns * 20;
156 | }
157 |
158 | public void show(Player player) {
159 | if(removeBaseLines)
160 | Bukkit.getScheduler().runTaskLaterAsynchronously(TabListTemplate.getPlugin(), () -> {
161 | removeBaseLines(player);
162 | }, 2);
163 |
164 | for(int x = 0; x < lines.length; x++) {
165 | for (int z = 0; z < lines[x].length; z++) {
166 | BodyLine line = lines[x][z];
167 | if(line == null) continue;
168 |
169 | line.show(player);
170 | }
171 | }
172 | }
173 |
174 | public void hide(Player player) {
175 | for(int x = 0; x < lines.length; x++) {
176 | for (int z = 0; z < lines[x].length; z++) {
177 | BodyLine line = lines[x][z];
178 | if(line == null) continue;
179 |
180 | line.hide(player);
181 | }
182 | }
183 | }
184 |
185 | public List linesAsList() {
186 | List lines = new ArrayList<>();
187 | for (BodyLine[] line : this.lines) {
188 | lines.addAll(Arrays.asList(line));
189 | }
190 | return lines;
191 | }
192 |
193 | public void reset(Player player) {
194 |
195 | List players = new ArrayList<>(Bukkit.getOnlinePlayers());
196 | for(Player online : players) {
197 | int z = 0;
198 | int index = players.indexOf(online);
199 |
200 | if(index >= 20) {
201 | if(index < 40) z = 1;
202 | if(index < 60) z = 2;
203 | }
204 |
205 | int x = index - MAX_LINES_PER_COLUMN * z;
206 |
207 | BodyLine line = new BodyLine(online.getPlayerListName(), x, z, online.getName(), ((CraftPlayer) player).getHandle())
208 | .setSkin(online.getUniqueId());
209 | line.show(player);
210 | }
211 | hide(player);
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/main/java/dev/fls/tablist/skin/SkinColor.java:
--------------------------------------------------------------------------------
1 | package dev.fls.tablist.skin;
2 |
3 | /**
4 | * Provides the Texture & Signature for the Heads to be displayed in the TabList Body.
5 | */
6 | public enum SkinColor {
7 |
8 | AQUA(
9 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODAyOTM2MywKICAicHJvZmlsZUlkIiA6ICIxYWZhZjc2NWI1ZGY0NjA3YmY3ZjY1ZGYzYWIwODhhOCIsCiAgInByb2ZpbGVOYW1lIiA6ICJMb3lfQmxvb2RBbmdlbCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9jOTk5NTQ4NzJkOTM3ZTQ0NzQ2NWQ3NzI0NjFjNDkxZDE1YmE2M2U0M2ZiOTFlMGNmZDkyZTJjYzFkZTRjYjU4IgogICAgfQogIH0KfQ==",
10 | "rjW7ryxH4hy4L7hwUlDNhhfQQcAbC2EONvB2e/foVIUvI0nnR+GymetSy0gDvQO8Hor57zontt/fHn7W+i314Kf4b/uqK+nWKUVubxDpwGrZby5fqg41wpRj3ipiRze3cKGIM2TN6J/wZMi3UJxzQuJzzW24jwzwcak29RsCqKyCzGVvBgp8AvQxJdmTRbFW9RBFOYB71H3VPdvbn/ASL//BEJsew3XVNWwmizDDKwuqPvFIteSioNPUC3hjXBXXUPQ/sxVjvei014jyY9ZelMrTYsnD6ocutHfdztG+Jm5q8HFfYr5WxZA8093iKJ06tb2/1izy4aXk80gsrUnG6G6aojH4o3LRS7Cr8MKjCOA5EIutlJpOXMyl+Hn0e1Advaj/cplic6OKGXGwrNzeos8JUH4TEw3034I/Dlwry249t2uKlKOfzXErOpCKbpAeW7hVNDjxqV7i6tDf/lnyMMgOqKVd9c+BxN94rB+qFiAhuj6qL5mmgcYHuL3OHQ4JIYa72IdHiTnBTFQ7ODwUfWQSLii92I5Mazn8uiauvcnvB2RSltjyHomVydpkX9RNAsGg1gYkDfvFPlPNPJZEXT7sYRyUJmn98XddtaUX12PxSh04A84J0dLoNUuIIS3Vi98jxC2MUgVkYu38gSOqFV+PJo2L6+ZolIxe58u50Oc="
11 | ),
12 | BLACK(
13 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODA3MjkwNSwKICAicHJvZmlsZUlkIiA6ICI4MWU4ZWU1MTE1ZmE0NzU5OTU1YTAzMThkNTNhNjViYyIsCiAgInByb2ZpbGVOYW1lIiA6ICJDb25zaWRlclRoYXQiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTA1ZTY2MDJjZTg1ZDU4Zjc3OWY5MmU3YTFlNjJmMGJlZjY5OGI2OTk4ZDEyNjUwMzVhZjdlY2JmZTJlZDE1MiIKICAgIH0KICB9Cn0=",
14 | "Snd3AC87Q57jlpmZO6yMOAq+vRdW8YR4LDuNGBBGFxVQ9XOzAWMoxCALcE8GeF+/zb5SUrcEKDIrPELcpvRqDmC5YyG4+QCSMiFZMObf2SSVHNqjtiQp3pbwjqUagEjIHPJyvgO/eQ/tDpccRMev/jx+pW7f7G+Hw94T2r6eQASQ4CAwtk/unWBCmbgujP6398aAFrPcSGtoYsm1R1giY43jcGn8Vhs6DMVvoI6kC8D/tkLTGon/d1T/iGoWe6rLayXGdfZPyw6HUUFIIU83vUdnSaZWR/H5OTZ8EeblF33UuwUVG8kq0dgx5BV5IZhXMuBTwnH2zFXI0P1mF4SqOthLgfHwGZ8wpVdMce74ft0wgbalfN5qTuVMzC5nvBYbNRNS9Xq3N/nq28V/BqtBaaJsb6aqJW8wtNGAe4nL4WShkRsnhm3uEzvIteWTWbxjDHHgDzmpHPZ6lUTkoqYOaYdQycHTGqJwYSbgmGgHdGcGqifiJdDBz3cCID7mNMBWTIfkxh1K3//huLJAUB4rXh7wPJQclaDlKYMO/Qegq3ci/EYeLMBbj7J1OvufUG5/OtpdyhowMUZqqz1xkEZ/+WlWfS+TVvnHrGSANSl/gobUuYPRGeF1JUDURjxSoDYQXDxOe2k7P9j27Z3NNgyVhOKzRT6FWz4pzkvQT8U/Er0="
15 | ),
16 | BLUE(
17 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODEyNjc2MSwKICAicHJvZmlsZUlkIiA6ICIxMWY3ZTIxMDMyOGE0MjRmYTU0NmFlZDk1M2FkNGU3ZCIsCiAgInByb2ZpbGVOYW1lIiA6ICJHb2RkZXNzd29sZjEiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjllYTAxNGM4ZGZhODdjZmM0YzNkNDViNmEwNDg3ODNlMWRjMjBlMGNmMDFhMGEzZWYyYTYyYzhmOGI0NiIKICAgIH0KICB9Cn0=",
18 | "algyza0T0nJYbcg302PQyK4cl3H7XEeG2xh+2uXK9mn/HYzb7vYGGKIZy1sZm7HempV4qfOVuUdCCMSeLjoTwGjnw9mnaSwsY/ytTBI3fPCVLDdZaxfBU7qUGLRIc+FT2MEUEmKQF/cr/ku/OqBmrWZPdMf8Z3EmUS1qW4jSc/pHTw/+82K59RgTO2QocVmBCzcF98t/g61Lf0avkh1xsdbVCSX7O4BWMasofTd8PNRiS7XElFWXWcUIw5DbMVwjPRB+kXk0nFt/l3IEjrnOGA+C/mUMmi2oNKucoSNLiq93HyXycXc94J13zYO4jM3Qc/YkRAz2SmN5jcD+wlTYnuaMojfBVRvmuT+KlDUY5EUwAHJkT3BLpk57I6qf1eeowzoRxjJAR1nZOZCn9NJjp06GV6xoNlAIuQNSmevbM2VbKoMoWYVgVTkqmORPwaj0CEjOMZrV39hCJKjFCHxr+PttOArjRcGW5sb0cLhrw8MSafGryWnX/njftNRvbr/eXwzH2JLqLFIkMAq0ixwhf3X8WTxam32lsQvh1BrWK13Xq5lcEMU2JtMggtOmwwJqm3EsWMLehPzQknt5zgyhbDVA/CKvfuarWmGBxgcmmlsHGzrgmBVUkW1Mdz/kT+C+DcSG+I7PZgwiklDsU/uRuWqCJl3kvBOI4umuiBSwplI="
19 | ),
20 | DARK_AQUA(
21 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODE4Nzc3OCwKICAicHJvZmlsZUlkIiA6ICIzYTNmNzhkZmExZjQ0OTllYjE5NjlmYzlkOTEwZGYwYyIsCiAgInByb2ZpbGVOYW1lIiA6ICJOb19jcmVyYXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2FlYThmYzhlMmY3NWFjNmU3NTU3YTA4NGI2MmM0MzgzOTI2MjA0YmIwZTNiZWRmNmNlZTE2N2IyNWU4NzFiMCIKICAgIH0KICB9Cn0=",
22 | "utHgW2dYl7gGHHqRkut8kZ1fVE7YrmDe5wSMaMpfN6JUPZJBPkb3HboLxU0ag1LvWZQpaYmXnrKM5fMihATlc48BjeHfzrYb1/zIQPF/MGazLqcRZ02Fu5BNZfPbf8ltlJInamhIrfhpXIoPef//gv6zP4PEZkMGgXg+ZZD7cVVi/4ug21m4xBQcix8tCYipNS/voQ7HzEJ9P7nBl9/HXLQRTlEM8AP0nuivX2BjqTTb35E0+0IyY/ZIWhQTwIEWXjxLfhlNQGkQk7AHryaUdExMN4ProOuIaU0O+BsbchI4aPWWgdzq7T0urjmN1AGFPjHqGca4a/pIIYjwtKqV287Jnxglkp2kXjgH6kT25F45xplM8F5HmvJVcWVJmfGhIzMEyxkaYYALwIrzh2j18ejy0iLFZ57rcNq88g32M1ll5yoULtTKbGkafR70Am0E9yABzu91KFXmDgkDQlwMFwpcdTO9BxiNvzhKeArAAHL0PgZwnf2DiysaZpwmLiwq6sKgaA2HpqsaxwwDK8qBCseTKnvOk5D8IXTE0YDWScUtcJQpVpRYlNPqqCubvdRlw2N3/t2ltqGdI6euSz2TrGZT8HrJ7r7sS32lY5hHqV8Ua07TLE9bQZYy/CqtpomQ3MkgFgZx++0PxhRMoiuL/FNSWl1uWpEADuFE9W4yef8="
23 | ),
24 | DARK_BLUE(
25 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODIyNTM2NSwKICAicHJvZmlsZUlkIiA6ICJkODAwZDI4MDlmNTE0ZjkxODk4YTU4MWYzODE0Yzc5OSIsCiAgInByb2ZpbGVOYW1lIiA6ICJ0aGVCTFJ4eCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS84MDExM2U4NjQ1MjVhYWQ0MjU0ZmJlM2VjNDQ5MzIwODRkOGUwZWRlYWVlMGE5ZWQzZDRhNmI1NzQ2ZTBiM2NkIgogICAgfQogIH0KfQ==",
26 | "lEZgrkxkSZE/s4zt0Qf2wFdKLvvHG2JxU4WBbsRRgdcK7EQFi0trhNoDK5khMqnQwwCssndL74vFk9oVcgoiXk/FtCBaawPeqZicdxkqqSh8me+BTmjK4l86akPAKVLXIfWjq8qLwEapAkEP+Zc6qMw+lYJwhy2gzt9cxUK+ALHarMKJv9zo4xU0WbZsLGE5PF8LDWTdIBErEnVl9CStZ6xBRg7+OLIofVka0nd0JF8cYLyHtbXYmj+MeySbCNTgqab2TKSRbPW+Xwbbc2JXOJqwXESFEsArlS+lCTydSwZK26QRfzXp/OWDddLculyutC0IO+yeGDZQ//XKDuvXM3O5OsSVYUCcett2AOYYUV+519UM5sARhfJL/F4J1t2W8TSvIIf9cb+usi/HjaSDfFCh1OPz0KOdggZX3Jdht1ssANCDPem6ZmLKN8tg4zq6+kGgEaiCoBOY86kai+zxOKoj2jxKeufw7qmE51J8MNz8yLfZWMdz9LfkKCIHxSpR+UZVCfIFevCu3Y/oCsKhyvpKYNNAvtBKDJVYcqSuQoZ9gx0PTFmvtZ806CEUauQHHI/W+ugZavz/8TjweF4i+2lob75rQyzt09cE9qKbFplStgVMxGJF7w0PEXIs4/bGmS3L8GkaaS/H6D5WQ1AYlGR8JRGguplgTS43uGqLQc0="
27 | ),
28 | DARK_GRAY(
29 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwNzc4OTIyOCwKICAicHJvZmlsZUlkIiA6ICJmMjU5MTFiOTZkZDU0MjJhYTcwNzNiOTBmOGI4MTUyMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJmYXJsb3VjaDEwMCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9jMTkyNTE3YjZkNzJmODMxMTU0NzE0MzdhYTlhZTgyOThjZmQ0NjQ3NzE0ZDg0MzNkMWFmYWI1OTA3MmIyODRlIgogICAgfQogIH0KfQ==",
30 | "AOpMmlO/TEBswd09MW3RJzjH3W2UlpOyQkW7VPWyLs1wswfIOHBda65iL4gHmpBPmRhnK3HeZAVCHnSxSCL53kC1b5DZS/RTnZE4jyY7G6TPLI8frNfAtKB7Zr+I22QsrvAUEhyRvgPFNdta5sdrq9DxBPhMgApuAxlwPlXoN9mEkiChFVW/EM/WqqYJu97KR9bpJjbWy78M1moVf+X+QZtB31KBbz70CrCXInjbBP0buLLtgLzQW4+wDw0UfnEK4bHbLE6U2lnSPvPeACWQjNeGoabp4GS8xCp4CH9f9p2jljrz9zpX4eOEXLvnGFix7sX1flEKT5ad0Y8zioc8aJRn5VBXKzcsWU/nDBo6rPjHkic5OtTOH/YjK7siYZa2aYmgmjob5yl9HV+lOCXyFwHBEXhILpw8e7tcyrnjhQ3tKPpbgLNJaIu43Dv5FntNxc9nDnC8EAy8/WMQ5RQ1kjUF9EmccIaewNw/kzIxKar++I5JRrCDwaTUK9/n0BN3vx6dDiC6y1hJrOjiSGz+HG4bqySDm1KZO/3GjoXmhE5KhFQVy5Rbrh5eoGqsBCR4dvrwYwbPWcbzVjoeVq8qEzHn+qkY+DS3X/b1rVpC9DyVyev0Bejg7seBDC5Vqlyg1tPJmE902Sv+s6pjm3IYfmj4YojtVOOm8p+kX8oYmBU="
31 | ),
32 | DARK_GREEN(
33 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODI4NjM5MiwKICAicHJvZmlsZUlkIiA6ICJkMGI4MjE1OThmMTE0NzI1ODBmNmNiZTliOGUxYmU3MCIsCiAgInByb2ZpbGVOYW1lIiA6ICJqYmFydHl5IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RhMjRlM2Y3ZDEwNmM2Mjk0MzY4OGUyNTMyMmIzNDZiYWU3ZjVhMmNhYzY4MjM3MmRiYzdiZTU5NTk3NjBkZDAiCiAgICB9CiAgfQp9",
34 | "XE/boxQokPioG9He/+513JSoWT8hTzaAnynF/qiRK0+zLAI+FhtcGEep0jZ74FFpKRTkyiI2GVfLXcZLpUO0gZetRYI3py0VFs9F6mWWEUWcafZh+XOmHOwGcEBpzZc24ETWz2S/mdOGbUsx3TBubeJ6VZwqL9VAIoWPxd+d66lzxA5yth/uPNsomVb8UsKzs6hjL1Gq1p/CyUj6xMpQTVzWS2/JI+v4yCTFTcm1A4PNgA/l4yw51nOC9betlR9lugbTYgoNNVwwM/4WRVgsghJRZBs7LkSFpQaoxSXBwRDK5UolM9AXyaatKqKBX5V7TVPxpaNUrsyDPWvR7Obqu4z1Ujaq2BqUdr8+pMuKmGH/Dr/5jDus/L9kL+PCXKeuFuCuVFD8fDWXq/0n3NNfZjhdttuCrVwn4aChNFVNl6A+sub+o/M259o8Y63tt23HszxqwpLbUBg+w0oyib1EvAlFFu4KlsTVocpQ8MFFCY77lXidSm92nARw9k4Kx3Hr+memiQ64qKctD7qHVegh+CmcZGp4bIlEVPU5p4TWLgTPzNB4Nhe3EwWsTIpBYCIPGYzhrAo+zKbeYqIYAeExP0YshT2lOn2Od8xeWNb1/VirdAAAV8+kOwzNL79Ukh8kZqqJ92XSp0LT+tmSwQyslKl88piZe08Rgh8qNQyZlDQ="
35 | ),
36 | DARK_PURPLE(
37 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODMyNTIxNCwKICAicHJvZmlsZUlkIiA6ICJkMWY2OTc0YzE2ZmI0ZjdhYjI1NjU4NzExNjM3M2U2NSIsCiAgInByb2ZpbGVOYW1lIiA6ICJGaW9saWVzdGEiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWM0ZWZiZGMwMmVlNGQ0NTIxNDgxNmFiYjRmODZkYjk2MjY4ZTgyZjhiZTAzNjNmMjRmMWQyYzU5OTI4M2EwMCIKICAgIH0KICB9Cn0=",
38 | "LGh1b2HjHP/zFFkmlypeoG44A/B52nC0Tz3yMk/FQB0raZtjgXLiliPpGoEJrG82WlxyPoHlczZKJI1eYlzLrP9scWfeF6pQ4uwpZ36mS+iW0foBm0V9YW/I+h7VmGA+aIHmx4avSECD+wrgxohEOrpwEm2CtVmVtPR/yfSJAb9W7E5B2Sf0fO3pZFMFjjtbe18wbe783/jxLqjxLWKs3ToYBPrjiKcv8J67PZ1i9olugdH1nIWi5/gYQpMgezpvxDNHVhMSnPHDRDPDIE2aAkDE+s7wNPqP4GZaOmd47Y14TXSZSSnuWNPSK826gi3jd6tMd42R9nxhnQ1hF9rVZYIvw4+OXhdt7OwU9IeivKldl4YWZjm452sPIkziqJGe5CaZXyaTQ0HuCZFJ2e9Fn4RdiRzsdqIOwx+fIQWNoSa9UKR8oP0Eru5oHAguCjfkwjKmBRUkgRHI2xu++B/gzlc5EDtaViXcd+i/6U2F6sfGYxdgSrkHlOvpER2o2jXEX0ar6c2wMOPzSy3JEU/McwEmQXKKz5Y4GkMn3SCoj9n+ObGnBtD48fGPCRXC9IJuupOJnK9pi7iGhbRJ3vC+C2lIa5HhciPfd8oe+6weg/uDdY8houo3gF7T24ZGN9zzj/tWYRW6PHuP/IIFGDtXC2+5c18Ek3Dq4fCBV6ARy88="
39 | ),
40 | DARK_RED(
41 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODM2NDc2OSwKICAicHJvZmlsZUlkIiA6ICJlZWJiNjkwNTk4ZDU0ZTYzOWVjMmQ3MmEzMzlhZGU4ZSIsCiAgInByb2ZpbGVOYW1lIiA6ICJpQU1fTU9ZIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2E4MDY4N2E3NmVhNThkMzIwY2M2N2YyODdkOGNkODZhYWJjYzcwZTQwNDVhMjM0MTY3M2JlY2UyMjYwMmYwOWMiCiAgICB9CiAgfQp9",
42 | "qkyWUTGZ+aQR0gKVy0jC+sVjYZW5NyAIWci25LzAhUlGox00ZW8kZLoYV3GsOckf0NTseXeYF7M5TS8GPeyAFxxWPnp2wUGKYqAIYIgY6KgQ8fulrxqkBtLxolh1uTXDRXoyViCaUOqNMbrqdEfuNPkKh6C0PFheEy6jdC16t2QcLDlvhr3rssrUdval21DjEWsNLVG0YspyGHIN5xRusu0tZxNF1iqVAseswmPDeaVNmqEg5ApLxtoDlLuqYtXUAkP6CuXg9oAaJbYvSXrjmaU81tZu9OfYE2rQ5QdKYPp5/2ZgNbsrjsWYNGwVnYDZi1IzZ1Tf7KigHc7yxqKpRrzWLxaks/9Nxu26KzK9Fw/W+aWyL5s20fPzpqh3CY9sbRuE8iqCUO8YpD0s5J2kZBBrzt1G88TRI22PXkNYNa7zQvVnEdkKbjx3hEgRt4TB5OiZXKLeOLYUgpyPX3ZwBe2jUT7JSubHfafKySWQ3QLnkTfYfks1E2bvEXUZghUM1eY9hLsLPCZoyiCeuHnt5TUxc6Yuc+bQeHE2qCSfJx13dlGVpNXvsM9mhunSw4DPZjVWNmyQgzdIp4CFfBEKSQeSvHIQUtJq5FuVKm6plsfhcYtwrsaQ/9xEHhI/MsbnJfOVTTVB+O2M6vDULprGA5jFwVxXjWdX7q1n1mv5Azw="
43 | ),
44 | GOLD(
45 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODQwOTgyNSwKICAicHJvZmlsZUlkIiA6ICJhNzdkNmQ2YmFjOWE0NzY3YTFhNzU1NjYxOTllYmY5MiIsCiAgInByb2ZpbGVOYW1lIiA6ICIwOEJFRDUiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzI1NmFiMmJjMDU3ZDM5NmIxZWVhODQ5ZThiZGFhZWNlYzU4YWJkOTQwZWE5NzgyMWMwZjk0MTQ5MmUzYzAyOCIKICAgIH0KICB9Cn0=",
46 | "JBDE8H3V6U9TMP2qxUpf06xzyVrayqE6hDp/x5AhZ8D9kiYlSPaWvkyhv63qzLrcF8NHhPp2NamwcPfkmZ40TZrEELjcMW7HxctxZtemPZ0OpVBLleW7qSy0Ta7aJ/H0BHiJ8fTWg6OuwMEwCRzC1qvr44dyCn1vZkK3Jy9+mlORga+IDcby44n36fpt0zc8w3yh2L3wlZjZg3ON5ByD+y2JmlgUdY8VoSzNygIycT1CrtHO4oBWEirquRCy7m7sAqlPIlOsfGNWVvBMcv6M3ZyeLymKoMdpFzYPW+n9lakQ64OSbIhfuXHXXFGBdQZNoW9LleOG8mP0Dqri7G+SNlQXyTqioGKA7vMH1/e/ZEuZtAHgdncu6oU+4+eVBb0aq3P7cMs8kirg5puLWG22Jq/f4CNZWCHRlkhbjMWKgA4aYmTIwKE2arYQ7hWoqkC6+a556kqNQC23zXFR8fnRL0qSQHqx0EKsKK17ITYXOUtfrJZ3BTqlx4+7zV454DltPkBPyrHwhlFFcFlTJAMrepXY85bTHanNnCJOMuLDKMzEE0cRvqtnNe2kwFVPwSLMQNFcCroMaOgw3XDlBeLpGVWKkhZDA8KlAjEZxet7amhHKXOOLNHlhuo7EwIRrxqxEZDXeuiGr5oWUbC7fm4E6HSt/I+tW52Lq9g8Rt4Fvy0="
47 | ),
48 | GRAY(
49 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODQ1NjcwNywKICAicHJvZmlsZUlkIiA6ICJmZDYwZjM2ZjU4NjE0ZjEyYjNjZDQ3YzJkODU1Mjk5YSIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWFkIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzU5ZGQ5OGNmNWRjYzQ0YjBmZWFmZTlhMTZmMWY1YTBlNzQ5MjU2NzFiOTRmMTRiYzFlNThiZTllMjI3Nzg1ZjAiCiAgICB9CiAgfQp9",
50 | "Y+LeppS1hEmuBiHKOITcy+HclZgBuWG4UIYhWm86UF3ts7cjOYu7EVHWEOXwWS49z/BZiRpS4y+WO/uk0bl9EksLk82m+fYve3Wfdh4Oii09RTvEhorNTNzxdApwlEaTmmFKZ4f7jOzdzbXe4yAb3OGMC/d4t0WDZ+ZMC84PzC0iYRgHUVMqis0N3RgoseNKe7/1lE5trrVmvoa/T8AsbHsOedPElhaeNWFCXYHTtsGtl611xTFftP+A4XLFGN/5oLHG3zahaqGNdscqtOy/WQWkN18HJe+v1bhzBj5XtmHRuTOJ5Jg2m3pGozplP7O8C0CwfP37hv6ih7wxlDjKGe57FW6jdzfBI7JdeqiHZGb2hC8F7S0l7cz/+7phkaiPgeEewyZBa+3K5tA89Ohd2j/TsRwceJJplA4+vyj2oUDXsHhq+5Xm//ys5eY4vzGTvwFOwAlF4dfvE8ku0XW5cWTLp2b52+z4wUOx8lIiBvfrBxsSdb2/5R7QbP2LdRsQhxGDBifEp4vDQKiGNbwDYn9i6/ERhvtehQlXsLXUGeqwIxuokN+VLlLrDMcGvTmE2oI7778uxV3l/3TEIG7iC0VMXts6Fs3O7cAXX5Z1Ph5I1CHM1O0+zKuYsSxP4sc7mzs/Pp+O5Fw5GUNMMuwv9asN1t6RwBGsBjRGmEUXVbY="
51 | ),
52 | GREEN(
53 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODUwMTcyOCwKICAicHJvZmlsZUlkIiA6ICI4YjgyM2E1YmU0Njk0YjhiOTE0NmE5MWRhMjk4ZTViNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJTZXBoaXRpcyIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS81MjFlOGZkYTgxNmYzZDQ4ZmE3NWQ4ZmRiNDdjMjAwNDNlNzdiM2RkNjI4NzkyMzJiOTAyN2IyZDVmNDQ4MmExIgogICAgfQogIH0KfQ==",
54 | "S2Mf0hiAVBqHUpl9DUIwxSxmghfI7o+EL/wZ4lEmiT1R1hzV8M8vZZMojO5FhBvzQcsFcTqb8421BJcMQYNJUVQDAmoIPHq654d67OP5GY5Iz1JxpsCpEkwzn++KWZeEmYl4II2z5U114VHKDt2pkRIhxPJmWmS3+VvSFMmPRYBO2X107jB3hrhKhRRRcLURvh9ry0b47SDXfzHF+dKP6BlZBtuIgaQ/IQNmqPH40+riR4ng3RL/filNgX/M7VWjHwaLoyiv7k2R7vW1jrSb2g57CVZEH1vQZ5+86MRHyQ+061yTZKXlt1MWDxQDnvfmmcliQ+1pKJ35VaaaxxzdWnlbbx3hh2dMZjGKuYtXpnPwDUJ/OV7iBGGd2SdjyVdsfKkP2ggdemkap7eZpcOVxek0a41/S5R9E5p0nIG+zdkRZ7W65fgH+H0oyZa62lnsSCAzSrI/cnz4BQthgg5dfUBS7R2m45ISty3vDVSYTAD0VAtjVqiNJ0pmU5qO4Jo0RFbj5I1HDiODqgDfbcKNCUgaJEedlbpcCOlPtUM7xVpRm1fVMfufyMJtZ3ZCdDB4/RNCRKqsCAbRrd7g54XI5eNA92t5mfUBi41pwM9+W3MBZQlLbs9+5rrTUwlO1mYcxQlzznhwC63Jw8HN9Usm3oqi1xNPOsdLWQqtB/oo8SM="
55 | ),
56 | LIGHT_PURPLE(
57 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODU0MjA4OSwKICAicHJvZmlsZUlkIiA6ICJkMWY2OTc0YzE2ZmI0ZjdhYjI1NjU4NzExNjM3M2U2NSIsCiAgInByb2ZpbGVOYW1lIiA6ICJGaW9saWVzdGEiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjczYjZmOWFkMGU2NTEwYjU2M2ZkYTA4NDI1ZjNlYzVkMTZhMzgwNzI3ZjE2MWIyYjg3M2ZhZmNmZjQ5MTZjNSIKICAgIH0KICB9Cn0=",
58 | "LflnHGT1KAcJAJ0LviXoJ4LsOymUhIkJmap3djfmYMB5+U8Skodc9dkmnMIaU7K3jAKrM87S2YJn22ZnDYPGXRbkgbJMrh6/hBeaEoogT/ZeNZcmo2iFLZkQeRdQqnd1b62lO0XKKdPqe5JTosNvX6+xJxkFl81/+cZKTxmyAUSycESYAJH4Vv/kY1dygH92O4vCf+FPjOOHX2Xy6zzaHOiJxqmGA0QcGWpNdrhoxORu5H4/FKdrjlqw72TinMC8KP6OBfLez1XzggaWVAHKAR/ry4dZ+YgzIJMOrt6xAgqJpCXt2STeLyqEMSLjSBP5J2F7DPImWRgmEuigYeXHWTrouVCTHBeeFfPRQ7WBq0gp0tEhZA4JgepMvg+sFthhPLEFuKifsTkeN/Q5/d+UxqVchQl/yqhY8Ug7aaUaMorqdPCp9Em3+fM6lASeUPOzlqt4LblMmZVY1P797XfUk+GkmNiUKUMo4lJAcahUdl3ja0msYNoW1l5id+yK22UWlA9riS63SSHch56+8N8uNCniaaq+HlMeIho3SBeHQGpb/nlycbA5+440GQEOsbqVwIY73eeA9AGf2Ose8BdPGgI1H6jECMB5l18lJbslUygUYY76sRs84Tbbh5/cpfcelS8qdkTx58P86Fi5Wl5Mxv9zWAzTueZNCyHMuivsCs8="
59 | ),
60 | RED(
61 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODU4MDc0NCwKICAicHJvZmlsZUlkIiA6ICI0ZGU4MjQxNjJkZTU0MzU5YWFlMDBmMzQ1ZmMyZTY0MSIsCiAgInByb2ZpbGVOYW1lIiA6ICJOYXRoYW5fS2luZyIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9hNjcwYmI3NGM5NzkwZThiOWVkNGJiMDJmMzBhZWRhODNjOWEyM2U2OGFmZmY2M2JhYmU4YThiMTM5OGMyZmRjIgogICAgfQogIH0KfQ==",
62 | "CviCV2EKBkM0K0jpr90rCX6l9ij6rJopRsiY8nTiPeDWgKcQzONVfiGQ56HBk3W5EaTqGe2WJuqF9AaaDiuYryhzam+p8/cUElxMlXcvkKF6BTNkWu4VqnfphUIAuUI7rMx9Ow1NEoObK/uWiC6kkxOrDF83ErxKsgCCaxqEy845Ii0OGcIWpwOqPRQD8Nu5XHdKlgI34aXApZVnSwfyrmrO/fmgD45/SW5khuVMPTWfReurhoVgJ345FNDlda3hFP5MI3wBxnlomID2YYjmVSfyXZM1/wYtIUrY0Sbt0948jlxZe0SL8uwek/fZDSoNPMm7r/qRClz/x/5EUuaak/tNpnMdzFWLNdNzyUwcAwzGnT8BuCaYKRtnV5tQH+XTehmdt8Fx0zsO3vRgna06JtLXwXro9Wz7CuQrgvIqZePjD95+stJPbW1eC/4SVOiNq3o+HIICSxM0cM/M97AAwU+8E/nriSNXEuqV+vmrQz+aKkiEOdiPed1Caxcghk8ZGsdmu0ep3zttoSDSfadIE6zw+4C0yVJ3qPtX1xpQW0lAKSFWqQ6Nht6mEIr1QpY7pys1y+dig7G2hTUeabjbZq/E/1xcLTgNhizomtHozlm14olTVWeqmgnyh0doLaNR0r2++pUICGvBGqODQa6dTcrYy5xozzs97EYGMQTe6QA="
63 | ),
64 | WHITE(
65 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODYyMTcwMCwKICAicHJvZmlsZUlkIiA6ICI3NTE0NDQ4MTkxZTY0NTQ2OGM5NzM5YTZlMzk1N2JlYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJUaGFua3NNb2phbmciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTZiZjE5NDNhMDVlZTY5M2FmYWIzNGJjYzg4NWNjN2VhM2RkODdiNTU3NWU2YTM1MTkxNGRjYmIwMTEwNzJlYiIKICAgIH0KICB9Cn0=",
66 | "ja86WuvK0vrQyQfWunZU4LWZEsGp7uUZhusIkceT9dsan68+J31VVUc5WWUgqGBjR2V2yOR7hl8lKjX5HgtLOjaJsYZx+TU6yyWa1HZ893Ag3gBekJOGd4B3h+4E2H/xTXZo3zkEDZHSTiCCWRdAZq4/t1zrZXrHhqftQHzOx32uWBmzMIm+WqQfU6sv0MUGDyUOYvKNndHeJLOcHk4ZoU0DL7nOlTJqSbwHnsz7MZ/nqXpquc1/AbceQQw2ooV5zYFYymOzuayei4+5SfIKFLlYb7ZUFS3I45+73KqQShaT3Hh3py+AThNTsHZL/LsFIr6/MGV0xpj0jQuEzwdjtPG3wbjGroe/xYT/L16i/pC/6B51JaDh82+laRHb4f/Ts3m8EPAQFmJUxipOosIVCYmgHfv7zlBIAviLNEW+p3WqdAaetEotNo5XvdePqZbB9z++ZOKStL92GAK3rNmzvRFC2j4Hdyt6Kmaod69yXK/eTsL8wr3DmNnVV2kIyOiPV/IziUbrV9enoYJvty+LprbOPzcbpt0tNKuYrF2OOY2EjRViJo+oO4QlvIqHgmK+MWBnRXuNkJSMuFo1qWIUM/qwFp0bX6edJXu4zhZeRDQJRB7WUqroDobQYjhtydQMdlei1uuyfFybFgWI6rjZsdlWbSJNE9yffYvElP1V4ak="
67 | ),
68 | YELLOW(
69 | "ewogICJ0aW1lc3RhbXAiIDogMTYzMzcwODY1Nzc3MSwKICAicHJvZmlsZUlkIiA6ICI0NWY3YTJlNjE3ODE0YjJjODAwODM5MmRmN2IzNWY0ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJfSnVzdERvSXQiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjcyZjMzODEyZGEwZTlhMDg5ZjRjYzI1ZGU1NTM5MWIzZjk3NTFhOWRlNTE3NTQxYzkzOWZmNjk2ZDBmMDc4NCIKICAgIH0KICB9Cn0=",
70 | "K4TNsYfYBawAh0M3OydjtqA405e3g+KFt8D+imqw2VMGtd99ShbdO57TmQMvgUxQFwlcPFBIqlQ2uGU12nstIS3R9cCXOhdEnY3d/7VUIxb2pKPIZhUB0PA0gNL49DaG+IR+/28roOV76Nai2fjvE5STWWhXvmlKWmSExu2eQu5alWGR8aSJTYLzlIVr26JazIWYBNOW8DGPMvqy6/dDKR/ASXuDs67mmb3aNyqp1TdX/I/NsabstD+7+GTT40vgADyFA8G0Oj6nXX9xoPYDH3dFbJR7xJRFv6293HoOO9x1uJIaM8VfLZR3SpTg1mww9CfwWMRqLM/oUGwVV7tbab3O5baYfPUSmwDva/vGBISruWVzVO+8TQNZBSs7ikIZO+MLzVO6OWHCAfRGRZgASmXzgFP3D7QPvxKh5uOWND9A9LfSa25y/tBWgRHLy/Pv3JDdkJVomYg5Pz35OjmDwS479PW8FqjSebG5SjefcjnL7MlKfBTyUtn/pz7u3mTTaT97NkzvoF3blxfnugpOUY7qc+mwH0Cf0nld769aOcWgz2WvyueeU6m3EKHpY/2LUhECwe2EayJ1eFtxUDIzJfBLx2nk8TrvXLv0XSsZabms94tWYazeIaZkgCV4G/MBmgJBYtdjMTzU7yXu0C7MbfDkJE/aT6EpdjdyLCddDmk="
71 | );
72 |
73 | private final String texture, signature;
74 |
75 | SkinColor(String texture, String signature) {
76 | this.texture = texture;
77 | this.signature = signature;
78 | }
79 |
80 | public String getTexture() {
81 | return texture;
82 | }
83 |
84 | public String getSignature() {
85 | return signature;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------