// Path: src/main/java/net/frozenbit/plugin5zig/cubecraft/gamemodes/CubeCraftGameMode.java
// public abstract class CubeCraftGameMode extends GameMode {
//     protected List<CubeCraftPlayer> players = new CopyOnWriteArrayList<>();
//     private String kit;
//     private boolean kitsEnabled;
//     private int pointsEarned;
// 
//     public CubeCraftPlayer getPlayerByName(String name) {
//         for (CubeCraftPlayer player : players) {
//             if (player.getName().equals(name)) {
//                 return player;
//             }
//         }
//         return null;
//     }
// 
//     /**
//      * @return A list of players currently in this gamemode. The list can be modified, however
//      * {@link #playerListUpdate()} MUST be called afterwards.
//      */
//     public List<CubeCraftPlayer> getPlayers() {
//         return players;
//     }
// 
//     public void playerListUpdate() {
//     }
// 
//     public String getKit() {
//         return kit;
//     }
// 
//     public void setKit(String kit) {
//         this.kit = kit;
//     }
// 
//     public boolean hasKitsEnabled() {
//         return kitsEnabled;
//     }
// 
//     public void setKitsEnabled(boolean kitsEnabled) {
//         this.kitsEnabled = kitsEnabled;
//     }
// 
//     public int getPointsEarned() {
//         return pointsEarned;
//     }
// 
//     public void addPointsEarned(int pointsEarned) {
//         this.pointsEarned += pointsEarned;
//     }
// 
//     public boolean isStalkerEnabled() {
//         return true;
//     }
// 
//     /**
//      * Return a singleton instance for the stalker of this gamemode. Before calling this method
//      * always check {@link #isStalkerEnabled()}.
//      * <p>
//      * Implementing classes that do not need a stalker must overwrite {@link #isStalkerEnabled()}
//      * and should throw an exception here.
//      *
//      * @return A stalker singleton.
//      */
//     public abstract Stalker getStalker();
// }
// 
// Path: src/main/java/net/frozenbit/plugin5zig/cubecraft/gamemodes/VotableCubeCraftGameMode.java
// public abstract class VotableCubeCraftGameMode extends CubeCraftGameMode {
//     private Table<CubeCraftPlayer, String, String> votes = HashBasedTable.create();
//     private Map<String, String> draw = new HashMap<>();
//     private TimeLootVotableMode.LootType chestType = TimeLootVotableMode.LootType.NONE;
//     private long lastDrawResultTime;
//     private ArrayList<String> formattedVoterList;
//     private String formattedVoteResult;
// 
//     protected abstract String formatVoteString(CubeCraftPlayer player, Map<String, String> vote);
// 
//     protected abstract String formatVoteResult(Map<String, String> draw);
// 
//     protected abstract boolean isShownInVoterList(CubeCraftPlayer player);
// 
//     protected abstract String[] getVoteCategories();
// 
//     public final long getVoteResultTime() {
//         return lastDrawResultTime;
//     }
// 
//     public final String getVoteResult() {
//         if (formattedVoteResult == null) {
//             formattedVoteResult = isDrawDone() ? formatVoteResult(Collections.unmodifiableMap(draw)) : "";
//         }
//         return formattedVoteResult;
//     }
// 
//     public boolean isDrawDone() {
//         for (String category : getVoteCategories()) {
//             if (!draw.containsKey(category)) {
//                 return false;
//             }
//         }
//         return true;
//     }
// 
//     public final List<String> getFormattedVoterList() {
//         if (formattedVoterList == null) {
//             formattedVoterList = new ArrayList<>();
//             for (CubeCraftPlayer player : players) {
//                 if (isShownInVoterList(player)) {
//                     formattedVoterList.add(formatVoteString(player, votes.row(player)));
//                 }
//             }
//         }
//         return formattedVoterList;
//     }
// 
//     @Override
//     public void playerListUpdate() {
//         super.playerListUpdate();
//         formattedVoterList = null;
//     }
// 
//     public final void onDrawResult(String category, String vote) {
//         draw.put(category, vote);
//         lastDrawResultTime = System.currentTimeMillis();
//         formattedVoteResult = null;
//     }
// 
//     public final void onVote(String playerName, String category, String vote) {
//         CubeCraftPlayer voter = getPlayerByName(playerName);
//         votes.put(voter, category, vote);
//         formattedVoterList = null;
//     }
// 
// }


import com.google.common.base.Splitter;
import eu.the5zig.mod.The5zigAPI;
import eu.the5zig.mod.server.GameState;
import eu.the5zig.mod.server.IPatternResult;
import eu.the5zig.mod.util.NetworkPlayerInfo;
import eu.the5zig.util.minecraft.ChatColor;
import net.frozenbit.plugin5zig.cubecraft.*;
import net.frozenbit.plugin5zig.cubecraft.gamemodes.CubeCraftGameMode;
import net.frozenbit.plugin5zig.cubecraft.gamemodes.VotableCubeCraftGameMode;
import java.util.*;
import java.util.regex.Pattern;
import static java.lang.String.format;
package net.frozenbit.plugin5zig.cubecraft.listeners;




/**
 * Abstract base class for games on CubeCraft. Handles player list changes and common game state changes.
 * All listener methods (<code>on*</code>) need to call super.
 *
 * @param <T> GameMode class the listener handles
 */
public abstract class AbstractCubeCraftGameListener<T extends CubeCraftGameMode> extends eu.the5zig.mod.server.AbstractGameListener<T> {
    private static final Pattern RANK_PATTERN = Pattern.compile(":(\\w+):");
    private static final Pattern TAG_PATTERN = Pattern.compile("\\[(\\w+)\\]");
    private static final int PLAYER_LIST_COOLDOWN = 60;
    private int cooldown;
    private boolean requestPlayerList;
    private String prefix;
    private boolean summaryShown;

    protected AbstractCubeCraftGameListener(String prefix) {
        this.prefix = prefix;
    }

    @Override
// Path: src/main/java/net/frozenbit/plugin5zig/cubecraft/gamemodes/CubeCraftGameMode.java
// public abstract class CubeCraftGameMode extends GameMode {
//     protected List<CubeCraftPlayer> players = new CopyOnWriteArrayList<>();
//     private String kit;
//     private boolean kitsEnabled;
//     private int pointsEarned;
// 
//     public CubeCraftPlayer getPlayerByName(String name) {
//         for (CubeCraftPlayer player : players) {
//             if (player.getName().equals(name)) {
//                 return player;
//             }
//         }
//         return null;
//     }
// 
//     /**
//      * @return A list of players currently in this gamemode. The list can be modified, however
//      * {@link #playerListUpdate()} MUST be called afterwards.
//      */
//     public List<CubeCraftPlayer> getPlayers() {
//         return players;
//     }
// 
//     public void playerListUpdate() {
//     }
// 
//     public String getKit() {
//         return kit;
//     }
// 
//     public void setKit(String kit) {
//         this.kit = kit;
//     }
// 
//     public boolean hasKitsEnabled() {
//         return kitsEnabled;
//     }
// 
//     public void setKitsEnabled(boolean kitsEnabled) {
//         this.kitsEnabled = kitsEnabled;
//     }
// 
//     public int getPointsEarned() {
//         return pointsEarned;
//     }
// 
//     public void addPointsEarned(int pointsEarned) {
//         this.pointsEarned += pointsEarned;
//     }
// 
//     public boolean isStalkerEnabled() {
//         return true;
//     }
// 
//     /**
//      * Return a singleton instance for the stalker of this gamemode. Before calling this method
//      * always check {@link #isStalkerEnabled()}.
//      * <p>
//      * Implementing classes that do not need a stalker must overwrite {@link #isStalkerEnabled()}
//      * and should throw an exception here.
//      *
//      * @return A stalker singleton.
//      */
//     public abstract Stalker getStalker();
// }
// 
// Path: src/main/java/net/frozenbit/plugin5zig/cubecraft/gamemodes/VotableCubeCraftGameMode.java
// public abstract class VotableCubeCraftGameMode extends CubeCraftGameMode {
//     private Table<CubeCraftPlayer, String, String> votes = HashBasedTable.create();
//     private Map<String, String> draw = new HashMap<>();
//     private TimeLootVotableMode.LootType chestType = TimeLootVotableMode.LootType.NONE;
//     private long lastDrawResultTime;
//     private ArrayList<String> formattedVoterList;
//     private String formattedVoteResult;
// 
//     protected abstract String formatVoteString(CubeCraftPlayer player, Map<String, String> vote);
// 
//     protected abstract String formatVoteResult(Map<String, String> draw);
// 
//     protected abstract boolean isShownInVoterList(CubeCraftPlayer player);
// 
//     protected abstract String[] getVoteCategories();
// 
//     public final long getVoteResultTime() {
//         return lastDrawResultTime;
//     }
// 
//     public final String getVoteResult() {
//         if (formattedVoteResult == null) {
//             formattedVoteResult = isDrawDone() ? formatVoteResult(Collections.unmodifiableMap(draw)) : "";
//         }
//         return formattedVoteResult;
//     }
// 
//     public boolean isDrawDone() {
//         for (String category : getVoteCategories()) {
//             if (!draw.containsKey(category)) {
//                 return false;
//             }
//         }
//         return true;
//     }
// 
//     public final List<String> getFormattedVoterList() {
//         if (formattedVoterList == null) {
//             formattedVoterList = new ArrayList<>();
//             for (CubeCraftPlayer player : players) {
//                 if (isShownInVoterList(player)) {
//                     formattedVoterList.add(formatVoteString(player, votes.row(player)));
//                 }
//             }
//         }
//         return formattedVoterList;
//     }
// 
//     @Override
//     public void playerListUpdate() {
//         super.playerListUpdate();
//         formattedVoterList = null;
//     }
// 
//     public final void onDrawResult(String category, String vote) {
//         draw.put(category, vote);
//         lastDrawResultTime = System.currentTimeMillis();
//         formattedVoteResult = null;
//     }
// 
//     public final void onVote(String playerName, String category, String vote) {
//         CubeCraftPlayer voter = getPlayerByName(playerName);
//         votes.put(voter, category, vote);
//         formattedVoterList = null;
//     }
// 
// }


// Path: src/main/java/net/frozenbit/plugin5zig/cubecraft/listeners/AbstractCubeCraftGameListener.java
import com.google.common.base.Splitter;
import eu.the5zig.mod.The5zigAPI;
import eu.the5zig.mod.server.GameState;
import eu.the5zig.mod.server.IPatternResult;
import eu.the5zig.mod.util.NetworkPlayerInfo;
import eu.the5zig.util.minecraft.ChatColor;
import net.frozenbit.plugin5zig.cubecraft.*;
import net.frozenbit.plugin5zig.cubecraft.gamemodes.CubeCraftGameMode;
import net.frozenbit.plugin5zig.cubecraft.gamemodes.VotableCubeCraftGameMode;
import java.util.*;
import java.util.regex.Pattern;
import static java.lang.String.format;

package net.frozenbit.plugin5zig.cubecraft.listeners;




/**
 * Abstract base class for games on CubeCraft. Handles player list changes and common game state changes.
 * All listener methods (<code>on*</code>) need to call super.
 *
 * @param <T> GameMode class the listener handles
 */
public abstract class AbstractCubeCraftGameListener<T extends CubeCraftGameMode> extends eu.the5zig.mod.server.AbstractGameListener<T> {
    private static final Pattern RANK_PATTERN = Pattern.compile(":(\\w+):");
    private static final Pattern TAG_PATTERN = Pattern.compile("\\[(\\w+)\\]");
    private static final int PLAYER_LIST_COOLDOWN = 60;
    private int cooldown;
    private boolean requestPlayerList;
    private String prefix;
    private boolean summaryShown;

    protected AbstractCubeCraftGameListener(String prefix) {
        this.prefix = prefix;
    }

    @Override
    public void onTick(CubeCraftGameMode gameMode) {