테스트

aion-server 4.8

Gitteol
최고관리자 · 1 · 💬 0 클론/새로받기
 4.8 61f661d · 1 commits 새로받기(Pull)
game-server/src/com/aionemu/gameserver/utils/chathandlers/ChatCommand.java
package com.aionemu.gameserver.utils.chathandlers;

import java.awt.Color;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aionemu.gameserver.configs.administration.CommandsConfig;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.network.aion.serverpackets.SM_MESSAGE;
import com.aionemu.gameserver.utils.ChatUtil;
import com.aionemu.gameserver.utils.PacketSendUtility;

/**
 * @author KID, Neon
 */
public abstract class ChatCommand {

	private static final Logger log = LoggerFactory.getLogger(ChatCommand.class);
	private final String prefix;
	private final String alias;
	private final String description;
	private String syntaxInfo;

	/**
	 * Initializes a chat command.
	 * 
	 * @param prefix
	 *          prefix for this command
	 * @param alias
	 *          command identifier
	 * @param description
	 *          short command description
	 */
	public ChatCommand(String prefix, String alias, String description) {
		this.prefix = prefix;
		this.alias = alias;
		this.description = description;
	}

	public final boolean run(Player player, String... params) {
		if (params.length == 1 && "help".equalsIgnoreCase(params[0])) {
			sendMessagePackets(player, "Command: " + ChatUtil.color(getAliasWithPrefix(), Color.WHITE) + "\n\t"
				+ (getDescription().isEmpty() ? "No description available." : getDescription()) + "\n" + getSyntaxInfo());
			return true;
		}

		try {
			try {
				execute(player, params);
			} catch (IllegalArgumentException e) {
				sendInfo(player, toErrorMessage(e));
			}
		} catch (Throwable t) {
			log.error("Exception executing chat command \"" + getAliasWithPrefix() + " " + String.join(" ", params) + "\" - Player: " + player.getName()
				+ ", Target: " + player.getTarget(), t);
			return false;
		}
		return true;
	}

	public final String getPrefix() {
		return prefix;
	}

	public final String getAlias() {
		return alias;
	}

	public final String getDescription() {
		return description;
	}

	public final String getAliasWithPrefix() {
		return prefix + alias;
	}

	/**
	 * Sets the command parameter info.<br>
	 * This parameter info is needed to generate the syntax info in {@link #sendInfo(Player, String...)}.<br>
	 * You can pass multiple comma separated lines of text. When following the parameter convention, parameters will be highlighted in white.
	 * 
	 * @param lines
	 *          strings may look like this:<br>
	 *          " - Short description for no parameter.",<br>
	 *          "&lt;param1&gt; &lt;param2&gt; [optionalParam3] - Short parameter description (two mandatory parameters, third one is optional).",<br>
	 *          "param1 &lt;param2&gt; - Short parameter description (first one is a non-variable word).",<br>
	 *          "Some other help text."<br>
	 */
	protected final void setSyntaxInfo(String... lines) {
		this.syntaxInfo = parseSyntaxInfo(lines);
	}

	public String getSyntaxInfo() {
		if (syntaxInfo == null) // init default info if handler did not set any syntax info
			setSyntaxInfo();
		return syntaxInfo;
	}

	private String parseSyntaxInfo(String... lines) {
		StringBuilder sb = new StringBuilder();
		sb.append("Syntax:");
		if (lines.length > 0) {
			boolean containsSquareBrackets = false;
			for (String info : lines) {
				String[] split = info.split(" - ", 2);
				if (split.length == 2) {
					if (!containsSquareBrackets && split[0].contains("["))
						containsSquareBrackets = true;
					sb.append("\n\t").append(ChatUtil.color(getAliasWithPrefix(), Color.WHITE)).append(' ');
					sb.append(split[0].replaceAll("([^<>\\[\\]| ]+)", ChatUtil.color("$1", Color.WHITE)).replace("[[color:f;", "[[color:f\u200B;").trim());
					sb.append(" - ");
					sb.append(split[1]);
				} else {
					sb.append("\n").append(info);
				}
			}
			if (containsSquareBrackets)
				sb.append("\nNote: Parameters enclosed in square brackets are optional.");
		} else {
			sb.append("\n\tNo syntax info available.");
		}
		return sb.toString();
	}

	public final byte getLevel() {
		Byte level = CommandsConfig.ACCESS_LEVELS.get(alias);
		if (level == null)
			throw new NullPointerException("Missing access level for " + getAliasWithPrefix());
		return level;
	}

	/**
	 * @param player
	 * @return True if player is allowed to use this command.
	 */
	abstract boolean validateAccess(Player player);

	/**
	 * Handles processing of a chat command.
	 * 
	 * @param player
	 * @param params
	 * @return True if command was executed.
	 */
	abstract boolean process(Player player, String... params);

	/**
	 * The code to be executed after successful command access validation. Any IllegalArgumentException and its subclasses will be catched, printing the
	 * error message to the player (using {@link #toErrorMessage(IllegalArgumentException)}).
	 */
	protected abstract void execute(Player player, String... params);

	/**
	 * This method can be overridden in case the default message extraction is not sufficient.
	 * 
	 * @return Message that should be sent to the player who caused the exception with invalid input. If null, the default syntax info will be sent, as
	 *         specified by {@link #sendInfo(Player, String...)}
	 */
	protected String toErrorMessage(IllegalArgumentException e) {
		String msg = e.getMessage();
		if (msg != null && msg.startsWith("No enum constant ")) { // "No enum constant com.aionemu.gameserver.model.siege.SiegeRace.invalidName"
			String[] enumParts = msg.substring(17).split("\\."); // -> ["com", "aionemu", "gameserver", "model", "siege", "SiegeRace", "invalidName"]
			String enumName = enumParts[enumParts.length - 2]; // -> "SiegeRace"
			// split camelCase word (https://stackoverflow.com/a/7599674)
			String[] enumNameParts = enumName.split("(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])"); // -> ["Siege", "Race"]
			enumName = String.join(" ", enumNameParts).toLowerCase(); // -> "siege race"
			msg = "Invalid " + enumName + ".";
		} else if (e instanceof NumberFormatException) { // Integer.parseInt and Long.parseLong don't provide nice error messages
			if (msg != null && msg.startsWith("For input string: "))
				msg = "Invalid number: " + msg.substring(18);
			else
				msg = "Invalid number.";
		}
		return msg;
	}

	/**
	 * Sends an info message to the player.<br>
	 * If no message parameter (or <tt>null</tt>) is specified, the default syntax info will be sent.<br>
	 * You can set syntax info via {@link #setSyntaxInfo(String...)}
	 * 
	 * @param player
	 *          player who will receive the message
	 * @param message
	 *          message text (insert newlines with \n or by passing comma separated strings)
	 */
	protected final void sendInfo(Player player, String... message) {
		StringBuilder sb = new StringBuilder();
		if (message.length > 1 || message.length == 1 && message[0] != null) {
			for (int i = 0; i < message.length; i++) {
				if (i > 0)
					sb.append('\n');
				sb.append(message[i]);
			}
		} else {
			sb.append(getSyntaxInfo());
		}
		sendMessagePackets(player, sb.toString());
	}

	/**
	 * Sends the formatted input message with as little packets as possible.
	 * 
	 * @param player
	 * @param message
	 */
	private static void sendMessagePackets(Player player, String message) {
		int lineLimit = 15; // length limit check alone is not safe if you send chat links (they can exceeded the display limit on client side)
		String[] lines = message.split("\n", -1);
		if (message.length() <= SM_MESSAGE.MESSAGE_SIZE_LIMIT && lines.length <= lineLimit) {
			PacketSendUtility.sendMessage(player, message);
		} else {
			StringBuilder sb = new StringBuilder(lines[0]);
			for (int i = 1; i < lines.length; i++) {
				if (i % lineLimit == 0 || sb.length() + 1 + lines[i].length() > SM_MESSAGE.MESSAGE_SIZE_LIMIT) { // current length + newLine char + next line length
					sendSafe(player, sb.toString());
					sb.setLength(0);
				} else {
					sb.append('\n');
				}
				sb.append(lines[i]);
			}
			sendSafe(player, sb.toString());
		}
	}

	/**
	 * Divides up the (single line) message if necessary and sends multiple packets (lines) to ensure to stay within the character limit per line.
	 */
	private static void sendSafe(Player player, String msg) {
		if (msg.length() > SM_MESSAGE.MESSAGE_SIZE_LIMIT) {
			int splitIndex = findSplitIndex(msg, ',', ']', ' ') + 1;
			PacketSendUtility.sendMessage(player, msg.substring(0, splitIndex));
			sendSafe(player, msg.substring(splitIndex));
		} else {
			PacketSendUtility.sendMessage(player, msg);
		}
	}

	private static int findSplitIndex(String msg, char... splitChars) {
		int searchStartIndex = Math.min(msg.length() / 2, SM_MESSAGE.MESSAGE_SIZE_LIMIT / 2);
		for (char splitChar : splitChars) {
			int splitIndex = msg.indexOf(splitChar, searchStartIndex);
			if (splitIndex > -1 && splitIndex <= SM_MESSAGE.MESSAGE_SIZE_LIMIT)
				return splitIndex;
		}
		return SM_MESSAGE.MESSAGE_SIZE_LIMIT;
	}

	/**
	 * Please use {@link #sendInfo(Player, String...)}.
	 * Old commands still override this method to show syntax info and should be ported eventually.
	 * TODO: remove this method when all commands are updated
	 */
	@Deprecated
	protected void info(Player player, String message) {
		throw new UnsupportedOperationException("Please don't call me and don't override me! Use sendInfo() instead. Syntax info can be initialized in constructor.");
	}
}

📎 첨부파일

댓글 작성 권한이 없습니다.
🏆 포인트 랭킹 TOP 10
순위 닉네임 포인트
1 no_profile 타키야겐지쪽지보내기 자기소개 아이디로 검색 전체게시물 102,949
2 no_profile 동가리쪽지보내기 자기소개 아이디로 검색 전체게시물 63,733
3 no_profile 라프텔쪽지보내기 자기소개 아이디로 검색 전체게시물 51,771
4 no_profile 불멸의행복쪽지보내기 자기소개 아이디로 검색 전체게시물 36,923
5 서번트쪽지보내기 자기소개 아이디로 검색 전체게시물 35,011
6 no_profile 닥터스쪽지보내기 자기소개 아이디로 검색 전체게시물 29,470
7 no_profile 검은고양이쪽지보내기 자기소개 아이디로 검색 전체게시물 29,077
8 no_profile Revolution쪽지보내기 자기소개 아이디로 검색 전체게시물 28,199
9 no_profile 보거스쪽지보내기 자기소개 아이디로 검색 전체게시물 26,731
10 no_profile 호롤롤로쪽지보내기 자기소개 아이디로 검색 전체게시물 17,020
알림 0