Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/reusable_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ jobs:
purpur
bukkit
spigot
game-versions: '1.21.11'
game-versions: '1.21.11>='
game-version-filter: none
retry-attempts: 3
retry-delay: 15000
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

public class NickCommand implements CommandExecutor, TabCompleter {

private static final PlainTextComponentSerializer PLAIN_TEXT_SERIALIZER = PlainTextComponentSerializer.plainText();
private static final String DIFFERENT_NAME_PERMISSION = "zpp.nick.different-name";
private static final Pattern VALID_NICKNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{3,16}$");

private static String joinArgs(String[] args, int start) {
StringBuilder builder = new StringBuilder();
Expand Down Expand Up @@ -63,6 +65,21 @@ private static String renderNamePreview(Player target, String rawTemplate) {
);
}

private static String getPlainName(String rawTemplate, String playerName) {
String normalizedTemplate = NameFormatUtil.normalizePlayerNameTemplate(rawTemplate);
return PLAIN_TEXT_SERIALIZER.serialize(
NameFormatUtil.applyPlayerPlaceholders(
NameFormatUtil.parseConfiguredComponent(normalizedTemplate),
playerName
)
).trim();
}

private static boolean isValidNickname(String rawTemplate, String playerName) {
String plainName = getPlainName(rawTemplate, playerName);
return VALID_NICKNAME_PATTERN.matcher(plainName).matches();
}

@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player player)) {
Expand Down Expand Up @@ -138,6 +155,11 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String
return false;
}

if (!isValidNickname(nameTemplate, target.getName())) {
Common.sendMMMessage(player, LanguageManager.getString("COMMAND.NICK.INVALID-NAME"));
return false;
}

applyNameTemplate(target, nameTemplate);
String formattedNamePreview = renderNamePreview(target, nameTemplate);

Expand Down Expand Up @@ -183,4 +205,4 @@ public List<String> onTabComplete(CommandSender sender, Command command, String
Collections.sort(completion);
return completion;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import dev.nandi0813.practice.manager.fight.match.enums.TeamEnum;
import dev.nandi0813.practice.manager.fight.util.FightPlayer;
import dev.nandi0813.practice.manager.fight.util.KitSelectionHandler;
import dev.nandi0813.practice.manager.fight.match.util.KitUtil;
import dev.nandi0813.practice.manager.ladder.abstraction.normal.NormalLadder;
import lombok.Getter;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

/**
* FFA-specific fight player that handles custom kit selection.
Expand All @@ -17,10 +19,14 @@
public class FFAFightPlayer extends FightPlayer {

private final FFA ffa;
private final NormalLadder ladder;
private NormalLadder ladder;

private KitSelectionHandler kitSelectionHandler;
private int chosenKit;
private int chosenKit = -1;

private ItemStack[] savedInventory;
private ItemStack[] savedArmor;
private ItemStack[] savedExtra;

public FFAFightPlayer(Player player, FFA ffa, NormalLadder ladder) {
super(player, ffa);
Expand All @@ -36,25 +42,42 @@ public FFAFightPlayer(Player player, FFA ffa, NormalLadder ladder) {

/**
* Displays the kit chooser GUI or applies the chosen kit.
* Used only for initial kit selection on join.
*/
public void showKitChooserOrApplyKit() {
if (this.kitSelectionHandler != null) {
this.kitSelectionHandler.showKitChooserOrApplyKit(TeamEnum.FFA);
} else {
// No custom kits, apply default kit directly
applyDefaultKit();
}
}

/**
* Restores the player's kit on death.
* Uses saved kit data if a custom kit was selected, otherwise applies the current ladder's default kit.
*/
public void restoreKitOnDeath() {
if (savedInventory != null) {
KitUtil.loadKit(player, TeamEnum.FFA, savedArmor, savedInventory, savedExtra);
} else {
KitUtil.loadDefaultLadderKit(player, TeamEnum.FFA, ladder);
}
}

/**
* Called when player clicks a kit slot to select it.
* After selection, the player becomes a full combatant.
*/
public void selectKit(int slot) {
if (this.kitSelectionHandler != null && this.kitSelectionHandler.getKits() != null
if (this.kitSelectionHandler != null && this.kitSelectionHandler.getKits() != null
&& this.kitSelectionHandler.getKits().containsKey(slot)) {
this.kitSelectionHandler.selectKit(slot, TeamEnum.FFA);
this.chosenKit = slot;

KitSelectionHandler handler = this.kitSelectionHandler;
savedInventory = cloneItems(handler.getKits().get(slot).getInventory());
savedArmor = cloneItems(handler.getKits().get(slot).getArmor());
savedExtra = cloneItems(handler.getKits().get(slot).getExtra());
}
}

Expand All @@ -73,5 +96,25 @@ public void applyDefaultKit() {
this.kitSelectionHandler = new KitSelectionHandler(player, getProfile(), ladder);
this.kitSelectionHandler.showKitChooserOrApplyKit(TeamEnum.FFA);
}
}

/**
* Resets the player's state for a new ladder (called on /ffa kit switch).
*/
public void resetForNewLadder(NormalLadder newLadder) {
this.ladder = newLadder;
this.chosenKit = -1;
this.savedInventory = null;
this.savedArmor = null;
this.savedExtra = null;
this.kitSelectionHandler = new KitSelectionHandler(player, getProfile(), newLadder);
}

private static ItemStack[] cloneItems(ItemStack[] source) {
if (source == null) return null;
ItemStack[] copy = source.clone();
for (int i = 0; i < copy.length; i++) {
if (copy[i] != null) copy[i] = copy[i].clone();
}
return copy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void addPlayer(Player player, NormalLadder ladder) {
return;

players.put(player, ladder);

// Use FFAFightPlayer to handle custom kit selection
FFAFightPlayer ffaFightPlayer = new FFAFightPlayer(player, this, ladder);
fightPlayers.put(player, ffaFightPlayer);
Expand Down Expand Up @@ -146,7 +146,7 @@ public void addPlayer(Player player, NormalLadder ladder) {

// Show kit chooser or apply default kit
ffaFightPlayer.showKitChooserOrApplyKit();

dev.nandi0813.practice.manager.fight.util.PlayerUtil.setAttackSpeed(player, ladder.getAttackCooldownModifier());

ProfileManager.getInstance().getProfile(player).setStatus(ProfileStatus.FFA);
Expand All @@ -161,6 +161,11 @@ public void changePlayerLadder(Player player, NormalLadder ladder) {
PlayerUtil.setFightPlayer(player, ladder);
KitUtil.loadDefaultLadderKit(player, TeamEnum.FFA, ladder);
dev.nandi0813.practice.manager.fight.util.PlayerUtil.setAttackSpeed(player, ladder.getAttackCooldownModifier());

FFAFightPlayer ffaFightPlayer = fightPlayers.get(player);
if (ffaFightPlayer != null) {
ffaFightPlayer.resetForNewLadder(ladder);
}
}

/**
Expand Down Expand Up @@ -264,7 +269,7 @@ public void killPlayer(Player player, Player killer, String deathMessage) {
private void applySelectedOrDefaultKit(Player player) {
FFAFightPlayer ffaFightPlayer = fightPlayers.get(player);
if (ffaFightPlayer != null) {
ffaFightPlayer.showKitChooserOrApplyKit();
ffaFightPlayer.restoreKitOnDeath();
return;
}

Expand Down Expand Up @@ -485,4 +490,4 @@ private void removePlayerFromBelowName(Player player) {
MatchManager.getInstance().getBelowNameManager().cleanUpForUser(player);
}

}
}
51 changes: 31 additions & 20 deletions core/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ SETUP:
# Queue settings
QUEUE:
COMBINED:
ENABLED: true # If true, lobby uses a single queue item instead of separate ranked and unranked items.
ENABLED: false # If true, lobby uses a single queue item instead of separate ranked and unranked items.
DEFAULT-GUI: "UNRANKED" # Options: UNRANKED, RANKED
SWITCH-ENABLED: true
SWITCH-ENABLED: false
MULTI:
ENABLED: true
PERMISSION: "zpp.queue.multi"
Expand Down Expand Up @@ -78,9 +78,11 @@ QUEUE:
- 31
- 32
- 33
- 38
- 39
- 40
- 41
- 42
CATEGORY-SELECTOR-SLOTS:
PVP: 3
MINIGAMES: 5
Expand All @@ -90,19 +92,21 @@ QUEUE:
ICON-MATERIAL: "DIAMOND_SWORD"
SELECTED-ICON-MATERIAL: "NETHERITE_SWORD"
LADDERS:
- "VANILLA"
- "SOUP"
- "GAPPLE"
- "NODEBUFF"
- "DEBUFF"
- "SPEARMACE"
- "MACE"
- "SPEARELYTRA"
- "SWORD"
- "CRYSTAL"
- "BOXING"
- "BUILDUHC"
- "AXE"
- "MACE"
- "SPEAR"
- "ARCHER"
- "GAPPLE"
- "SG"
- "BUILDUHC"
- "CRYSTAL"
- "ARCHER"
- "SOUP"
- "VANILLA"
- "NODEBUFF"
- "DEBUFF"
MINIGAMES:
DISPLAY-NAME: "MINIGAMES"
ICON-MATERIAL: "SLIME_BALL"
Expand All @@ -120,7 +124,7 @@ QUEUE:
- "SKYWARS"
RANKED:
CUSTOM-KIT:
ENTRY-ENABLED: true # If true, ranked selector also shows the custom-kit queue entry.
ENTRY-ENABLED: false # If true, ranked selector also shows the custom-kit queue entry.
MAX-QUEUE-TIME: 300 # In sec.
GUI-UPDATE-MINUTE: 1
DEFAULT-ELO: 1000
Expand All @@ -145,9 +149,11 @@ QUEUE:
- 31
- 32
- 33
- 38
- 39
- 40
- 41
- 42
CATEGORY-SELECTOR-SLOTS:
PVP: 3
MINIGAMES: 5
Expand All @@ -157,16 +163,21 @@ QUEUE:
ICON-MATERIAL: "DIAMOND_SWORD"
SELECTED-ICON-MATERIAL: "NETHERITE_SWORD"
LADDERS:
- "SOUP"
- "SPEARMACE"
- "MACE"
- "SPEARELYTRA"
- "SWORD"
- "CRYSTAL"
- "BOXING"
- "BUILDUHC"
- "AXE"
- "GAPPLE"
- "SG"
- "ARCHER"
- "SOUP"
- "VANILLA"
- "NODEBUFF"
- "DEBUFF"
- "BOXING"
- "MACE"
- "SPEAR"
- "SG"
- "BUILDUHC"
- "CRYSTAL"
MINIGAMES:
DISPLAY-NAME: "MINIGAMES"
ICON-MATERIAL: "SLIME_BALL"
Expand Down
29 changes: 14 additions & 15 deletions core/src/main/resources/ladders/archer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,30 @@ type: BASIC
settings:
regen: false
hunger: false
ranked: false
weightClass: UNRANKED_AND_RANKED
editable: true
drop-inventory: true
multiRoundStartCountdown: true
hitdelay: 1.0
rounds: 1
epcooldown: 13.0
startcountdown: 5
knockback: DEFAULT
hearts: 10
maxduration: 900
epcooldown: 0.0
gacooldown: 0.0
drop-inventory: true
fireworkcooldown: 1.0
windchargecooldown: 0.0
startcountdown: 3
startmove: true
matchtypes:
- DUEL
- PARTY_FFA
- PARTY_SPLIT
- PARTY_VS_PARTY
weightClass: UNRANKED_AND_RANKED
multiRoundStartCountdown: true
maxduration: 900
fireworkcooldown: 1.0
startmove: true
knockback: DEFAULT
tntfusetime: 4
healthbelowname: false
resetbuildafterround: false
breakallblocks: false
hearts: 10
windchargecooldown: 0.0
roundenddelay: 3
roundstatustitles: true
countdowntitles: true
Expand All @@ -37,12 +36,12 @@ custom-kit-extra-items:
unranked: rO0ABXcEAAAAHHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHA=
icon:
==: org.bukkit.inventory.ItemStack
DataVersion: 4790
DataVersion: 4903
id: minecraft:bow
count: 1
components:
minecraft:custom_name: '{extra:[{bold:0b,color:"gold",italic:0b,obfuscated:0b,strikethrough:0b,text:"Archer",underlined:0b}],text:""}'
schema_version: 1
armor: rO0ABXcEAAAABHNyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAkwABGtleXN0ABJMamF2YS9sYW5nL09iamVjdDtMAAZ2YWx1ZXNxAH4ABHhwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAABnQAAj09dAALRGF0YVZlcnNpb250AAJpZHQABWNvdW50dAAKY29tcG9uZW50c3QADnNjaGVtYV92ZXJzaW9udXEAfgAGAAAABnQAHm9yZy5idWtraXQuaW52ZW50b3J5Lkl0ZW1TdGFja3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAABK2dAAUbWluZWNyYWZ0Omlyb25fYm9vdHNzcQB+ABAAAAABc3IAF2phdmEudXRpbC5MaW5rZWRIYXNoTWFwNMBOXBBswPsCAAFaAAthY2Nlc3NPcmRlcnhyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAACdAAVbWluZWNyYWZ0OnJlcGFpcl9jb3N0dAABMXQAFm1pbmVjcmFmdDplbmNoYW50bWVudHN0ACV7Im1pbmVjcmFmdDpwcm9qZWN0aWxlX3Byb3RlY3Rpb24iOjF9eABxAH4AFHNxAH4AAHNxAH4AA3VxAH4ABgAAAAZxAH4ACHEAfgAJcQB+AApxAH4AC3EAfgAMcQB+AA11cQB+AAYAAAAGcQB+AA9zcQB+ABAAABK2dAAXbWluZWNyYWZ0Omlyb25fbGVnZ2luZ3NxAH4AFHNxAH4AFT9AAAAAAAAMdwgAAAAQAAAAAnQAFW1pbmVjcmFmdDpyZXBhaXJfY29zdHQAATF0ABZtaW5lY3JhZnQ6ZW5jaGFudG1lbnRzdAAleyJtaW5lY3JhZnQ6cHJvamVjdGlsZV9wcm90ZWN0aW9uIjoxfXgAcQB+ABRzcQB+AABzcQB+AAN1cQB+AAYAAAAGcQB+AAhxAH4ACXEAfgAKcQB+AAtxAH4ADHEAfgANdXEAfgAGAAAABnEAfgAPc3EAfgAQAAAStnQAGW1pbmVjcmFmdDppcm9uX2NoZXN0cGxhdGVxAH4AFHNxAH4AFT9AAAAAAAAMdwgAAAAQAAAAAnQAFW1pbmVjcmFmdDpyZXBhaXJfY29zdHQAATF0ABZtaW5lY3JhZnQ6ZW5jaGFudG1lbnRzdAAleyJtaW5lY3JhZnQ6cHJvamVjdGlsZV9wcm90ZWN0aW9uIjoxfXgAcQB+ABRzcQB+AABzcQB+AAN1cQB+AAYAAAAGcQB+AAhxAH4ACXEAfgAKcQB+AAtxAH4ADHEAfgANdXEAfgAGAAAABnEAfgAPc3EAfgAQAAAStnQAFW1pbmVjcmFmdDppcm9uX2hlbG1ldHEAfgAUc3EAfgAVP0AAAAAAAAx3CAAAABAAAAACdAAVbWluZWNyYWZ0OnJlcGFpcl9jb3N0dAABMXQAFm1pbmVjcmFmdDplbmNoYW50bWVudHN0ACV7Im1pbmVjcmFmdDpwcm9qZWN0aWxlX3Byb3RlY3Rpb24iOjF9eABxAH4AFA==
inventory: rO0ABXcEAAAAJHNyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAkwABGtleXN0ABJMamF2YS9sYW5nL09iamVjdDtMAAZ2YWx1ZXNxAH4ABHhwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAABnQAAj09dAALRGF0YVZlcnNpb250AAJpZHQABWNvdW50dAAKY29tcG9uZW50c3QADnNjaGVtYV92ZXJzaW9udXEAfgAGAAAABnQAHm9yZy5idWtraXQuaW52ZW50b3J5Lkl0ZW1TdGFja3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAABK2dAANbWluZWNyYWZ0OmJvd3NxAH4AEAAAAAFzcgAXamF2YS51dGlsLkxpbmtlZEhhc2hNYXA0wE5cEGzA+wIAAVoAC2FjY2Vzc09yZGVyeHIAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAADHcIAAAAEAAAAAJ0ABVtaW5lY3JhZnQ6cmVwYWlyX2Nvc3R0AAEzdAAWbWluZWNyYWZ0OmVuY2hhbnRtZW50c3QAMXsibWluZWNyYWZ0OmluZmluaXR5IjoxLCJtaW5lY3JhZnQ6dW5icmVha2luZyI6M314AHEAfgAUcHBwcHBwcHBzcQB+AABzcQB+AAN1cQB+AAYAAAAFcQB+AAhxAH4ACXEAfgAKcQB+AAtxAH4ADXVxAH4ABgAAAAVxAH4AD3NxAH4AEAAAErZ0AA9taW5lY3JhZnQ6YXJyb3dxAH4AFHEAfgAUcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHA=
armor: rO0ABXcEAAAABHNyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAkwABGtleXN0ABJMamF2YS9sYW5nL09iamVjdDtMAAZ2YWx1ZXNxAH4ABHhwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAABnQAAj09dAALRGF0YVZlcnNpb250AAJpZHQABWNvdW50dAAKY29tcG9uZW50c3QADnNjaGVtYV92ZXJzaW9udXEAfgAGAAAABnQAHm9yZy5idWtraXQuaW52ZW50b3J5Lkl0ZW1TdGFja3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAABMndAAUbWluZWNyYWZ0Omlyb25fYm9vdHNzcQB+ABAAAAABc3IAF2phdmEudXRpbC5MaW5rZWRIYXNoTWFwNMBOXBBswPsCAAFaAAthY2Nlc3NPcmRlcnhyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAACdAAVbWluZWNyYWZ0OnJlcGFpcl9jb3N0dAABMXQAFm1pbmVjcmFmdDplbmNoYW50bWVudHN0ACV7Im1pbmVjcmFmdDpwcm9qZWN0aWxlX3Byb3RlY3Rpb24iOjF9eABxAH4AFHNxAH4AAHNxAH4AA3VxAH4ABgAAAAZxAH4ACHEAfgAJcQB+AApxAH4AC3EAfgAMcQB+AA11cQB+AAYAAAAGcQB+AA9zcQB+ABAAABMndAAXbWluZWNyYWZ0Omlyb25fbGVnZ2luZ3NxAH4AFHNxAH4AFT9AAAAAAAAMdwgAAAAQAAAAAnQAFW1pbmVjcmFmdDpyZXBhaXJfY29zdHQAATF0ABZtaW5lY3JhZnQ6ZW5jaGFudG1lbnRzdAAleyJtaW5lY3JhZnQ6cHJvamVjdGlsZV9wcm90ZWN0aW9uIjoxfXgAcQB+ABRzcQB+AABzcQB+AAN1cQB+AAYAAAAGcQB+AAhxAH4ACXEAfgAKcQB+AAtxAH4ADHEAfgANdXEAfgAGAAAABnEAfgAPc3EAfgAQAAATJ3QAGW1pbmVjcmFmdDppcm9uX2NoZXN0cGxhdGVxAH4AFHNxAH4AFT9AAAAAAAAMdwgAAAAQAAAAAnQAFW1pbmVjcmFmdDpyZXBhaXJfY29zdHQAATF0ABZtaW5lY3JhZnQ6ZW5jaGFudG1lbnRzdAAleyJtaW5lY3JhZnQ6cHJvamVjdGlsZV9wcm90ZWN0aW9uIjoxfXgAcQB+ABRzcQB+AABzcQB+AAN1cQB+AAYAAAAGcQB+AAhxAH4ACXEAfgAKcQB+AAtxAH4ADHEAfgANdXEAfgAGAAAABnEAfgAPc3EAfgAQAAATJ3QAFW1pbmVjcmFmdDppcm9uX2hlbG1ldHEAfgAUc3EAfgAVP0AAAAAAAAx3CAAAABAAAAACdAAVbWluZWNyYWZ0OnJlcGFpcl9jb3N0dAABMXQAFm1pbmVjcmFmdDplbmNoYW50bWVudHN0ACV7Im1pbmVjcmFmdDpwcm9qZWN0aWxlX3Byb3RlY3Rpb24iOjF9eABxAH4AFA==
inventory: rO0ABXcEAAAAJHNyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAkwABGtleXN0ABJMamF2YS9sYW5nL09iamVjdDtMAAZ2YWx1ZXNxAH4ABHhwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAABnQAAj09dAALRGF0YVZlcnNpb250AAJpZHQABWNvdW50dAAKY29tcG9uZW50c3QADnNjaGVtYV92ZXJzaW9udXEAfgAGAAAABnQAHm9yZy5idWtraXQuaW52ZW50b3J5Lkl0ZW1TdGFja3NyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAABMndAANbWluZWNyYWZ0OmJvd3NxAH4AEAAAAAFzcgAXamF2YS51dGlsLkxpbmtlZEhhc2hNYXA0wE5cEGzA+wIAAVoAC2FjY2Vzc09yZGVyeHIAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAADHcIAAAAEAAAAAJ0ABVtaW5lY3JhZnQ6cmVwYWlyX2Nvc3R0AAEzdAAWbWluZWNyYWZ0OmVuY2hhbnRtZW50c3QAMXsibWluZWNyYWZ0OmluZmluaXR5IjoxLCJtaW5lY3JhZnQ6dW5icmVha2luZyI6M314AHEAfgAUcHBwcHBwcHBzcQB+AABzcQB+AAN1cQB+AAYAAAAFcQB+AAhxAH4ACXEAfgAKcQB+AAtxAH4ADXVxAH4ABgAAAAVxAH4AD3NxAH4AEAAAEyd0AA9taW5lY3JhZnQ6YXJyb3dxAH4AFHEAfgAUcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHA=
extra: rO0ABXcEAAAAAXA=
Loading
Loading