/*
* This file is part of aion-emu <aion-emu.com>.
*
* aion-emu is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* aion-emu is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with aion-emu. If not, see <http://www.gnu.org/licenses/>.
*/
package admincommands;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.log4j.Logger;
import com.aionemu.gameserver.GameServerError;
import com.aionemu.gameserver.configs.administration.AdminConfig;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.network.aion.serverpackets.SM_CUSTOM_PACKET;
import com.aionemu.gameserver.network.aion.serverpackets.SM_CUSTOM_PACKET.PacketElementType;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.ThreadPoolManager;
import com.aionemu.gameserver.utils.chathandlers.AdminCommand;
/**
* This admin command is used for sending custom packets from server to client.
* <p/>
* Sends packets based on xml mappings in folder "./data/packets".<br />
* Command details: "//send [1]<br />
* * 1 - packet mappings name.<br />
* * - 'demo' for file './data/packets/demo.xml'<br />
* * - 'test' for file './data/packets/test.xml'<br />
* * Reciever is a targetted by admin player. If target is 'null' or not a Player - sends to admin.<br />
* <p/>
* Created on: 14.07.2009 13:54:46
*
* @author Aquanox
*/
public class AdvSendFakeServerPacket extends AdminCommand
{
private static final Logger logger = Logger.getLogger(AdvSendFakeServerPacket.class);
private static final File FOLDER = new File("./data/packets");
private Unmarshaller unmarshaller;
/**
* Create an instance of admin command.
*
* @throws GameServerError on initialization error
*/
public AdvSendFakeServerPacket()
{
super("send");
// init unmrshaller once.
try
{
unmarshaller = JAXBContext.newInstance(Packets.class, Packet.class, Part.class).createUnmarshaller();
}
catch (Exception e)
{
throw new GameServerError("Failed to initialize unmarshaller.", e);
}
}
/** {@inheritDoc} */
@Override
public void executeCommand(final Player admin, String[] params)
{
if(admin.getAccessLevel() < AdminConfig.COMMAND_ADVSENDFAKESERVERPACKET)
{
PacketSendUtility.sendMessage(admin, "You dont have enough rights to execute this command");
return;
}
if (params.length != 1)
{
PacketSendUtility.sendMessage(admin, "Example: //send [file] ");
return;
}
final String mappingName = params[0];
final Player target = getTargetPlayer(admin);
//logger.debug("Mapping: " + mappingName);
//logger.debug("Target: " + target);
File packetsData = new File(FOLDER, mappingName + ".xml");
if (!packetsData.exists())
{
PacketSendUtility.sendMessage(admin, "Mapping with name " + mappingName + " not found");
return;
}
final Packets packetsTemplate;
try
{
packetsTemplate = (Packets) unmarshaller.unmarshal(packetsData);
}
catch (JAXBException e)
{
logger.error("Unmarshalling error", e);
return;
}
if (packetsTemplate.getPackets().isEmpty())
{
PacketSendUtility.sendMessage(admin, "No packets to send.");
return;
}
send(admin, target, packetsTemplate);
}
private void send(Player sender,final Player target, Packets packets)
{
final String senderObjectId = String.valueOf(sender.getObjectId());
final String targetObjectId = String.valueOf(target.getObjectId());
int packetIndex = 0;// first packet should be sent immediately.
for (final Packet packetTemplate : packets)
{
//logger.debug("Processing: " + packetTemplate);
final SM_CUSTOM_PACKET packet = new SM_CUSTOM_PACKET(packetTemplate.getOpcode());
for (Part part : packetTemplate.getParts())
{
PacketElementType byCode = PacketElementType.getByCode(part.getType());
String value = part.getValue();
if (value.indexOf("${objectId}") != -1)
value = value.replace("${objectId}", targetObjectId);
if (value.indexOf("${senderObjectId}") != -1)
value = value.replace("${senderObjectId}", senderObjectId);
if (value.indexOf("${targetObjectId}") != -1)
value = value.replace("${targetObjectId}", targetObjectId);
if (part.getRepeatCount() == 1) // skip loop
{
packet.addElement(byCode, value);
}
else
{
for (int i = 0; i < part.getRepeatCount(); i++)
packet.addElement(byCode, value);
}
}
ThreadPoolManager.getInstance().schedule(new Runnable()
{
@Override
public void run()
{
//logger.debug("Sending: " + packetTemplate);
PacketSendUtility.sendPacket(target, packet);
}
}, packetIndex * packets.getDelay()); //Kamui: this is correct or a mistake?
packetIndex++;
}
}
private Player getTargetPlayer(Player admin)
{
if (admin.getTarget() instanceof Player)
return (Player) admin.getTarget();
else
return admin;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "packets")
private static class Packets implements Iterable<Packet>
{
@XmlElement(name = "packet")
private List<Packet> packets = new ArrayList<Packet>();
@XmlAttribute(name = "delay")
private long delay = -1;
public long getDelay()
{
return delay;
}
public List<Packet> getPackets()
{
return packets;
}
@SuppressWarnings("unused")
public boolean add(Packet packet)
{
return packets.add(packet);
}
@Override
public Iterator<Packet> iterator()
{
return packets.iterator();
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append("Packets");
sb.append("{delay=").append(delay);
sb.append(", packets=").append(packets);
sb.append('}');
return sb.toString();
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "packet")
private static class Packet
{
@XmlElement(name = "part")
private Collection<Part> parts = new ArrayList<Part>();
@XmlAttribute(name = "opcode")
private String opcode = "-1";
public int getOpcode()
{
return Integer.decode(opcode);
}
public Collection<Part> getParts()
{
return parts;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append("Packet");
sb.append("{opcode=").append(opcode);
sb.append(", parts=").append(parts);
sb.append('}');
return sb.toString();
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "part")
private static class Part
{
@XmlAttribute(name = "type", required = true)
private String type = null;
@XmlAttribute(name = "value", required = true)
private String value = null;
@XmlAttribute(name = "repeat", required = true)
private int repeatCount = 1;
public char getType()
{
return type.charAt(0);
}
public String getValue()
{
return value;
}
public int getRepeatCount()
{
return repeatCount;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append("Part");
sb.append("{type='").append(type).append('\'');
sb.append(", value='").append(value).append('\'');
sb.append(", repeatCount=").append(repeatCount);
sb.append('}');
return sb.toString();
}
}
}