package server;
import java.awt.Color;
import java.awt.Point;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.crypto.Cipher;
import server.command.CommandProcessor;
import server.command.MailCommand;
import server.command.RoomCommand;
import server.scripting.Script;
import server.scripting.ScriptingLanguage;
import server.scripting.ScriptingSystem;
import server.token.TokenString;
import common.ByteSource;
import common.Face;
import common.PubGroupMember;
import common.PubRoom;
import common.PubRoomEntity;
import common.ScriptType;
import common.TextEditorType;
import common.clientevent.ClientEvent;
import common.clientevent.ClientEventType;
import common.clientevent.CommandClientEvent;
import common.clientevent.EditRoomClientEvent;
import common.clientevent.ExecuteManualScriptClientEvent;
import common.clientevent.FaceChooserClientEvent;
import common.clientevent.ImageUploadClientEvent;
import common.clientevent.MailCreateClientEvent;
import common.clientevent.SaveScriptClientEvent;
import common.gameevent.ClearScreenGameEvent;
import common.gameevent.CommandLineColorGameEvent;
import common.gameevent.ExecuteManualScriptResponseGameEvent;
import common.gameevent.FaceChooserGameEvent;
import common.gameevent.FaceGameEvent;
import common.gameevent.GroupGameEvent;
import common.gameevent.ImageGameEvent;
import common.gameevent.ImageUploadGameEvent;
import common.gameevent.LocationGameEvent;
import common.gameevent.RoomDestroyedGameEvent;
import common.gameevent.RoomEditorGameEvent;
import common.gameevent.RoomEntityGameEvent;
import common.gameevent.RoomUpdatedGameEvent;
import common.gameevent.SaveScriptResponseGameEvent;
import common.gameevent.ScriptEditorGameEvent;
import common.gameevent.StatGameEvent;
import common.gameevent.TableGameEvent;
import common.gameevent.TextEditorGameEvent;
import common.gameevent.TextGameEvent;
import common.gameevent.TrackGameEvent;
public class ClientState implements RoomListener, TrackingListener, GroupListener, AreaListener {
private final Player player;
private final Client client;
private Area area;
private Room room;
private Group group;
private Set<Player> trackSet;
private Set<RoomEntity> roomEntitySet = new HashSet<RoomEntity>();
private Set<Player> groupMemberSet = new HashSet<Player>();
private boolean closed;
private ClientState(SelectionKey key, Player player, Cipher encipher, Cipher decipher) {
this.player = player;
this.client = new Client(key, this, encipher, decipher);
key.attach(client);
player.goOnline(this);
setGroup(player.getGroup());
setRoom(player.getRoom());
send(area.makeMapGameEvent());
sendLocationGameEvent();
recalcCommandLineColor();
recalcTrackingMode();
String loginCommand = System.getProperty("aelfengard.loginCommand");
if (loginCommand != null && player.getId() != 1) {
try {
Runtime.getRuntime().exec(loginCommand);
}
catch (IOException ex) {
}
}
}
public void close(boolean replaced) {
if (closed) {
return;
}
closed = true;
client.close();
if (trackSet != null) {
TrackingSystem.removeTrackingListener(this);
}
if (room != null) {
room.removeRoomListener(this);
}
if (area != null) {
area.removeAreaListener(this);
}
if (group != null) {
group.removeGroupListener(this);
}
if (!replaced) {
player.goOffline();
}
}
public static void init(SelectionKey key, Player player, Cipher encipher, Cipher decipher) {
new ClientState(key, player, encipher, decipher);
}
public void sendText(boolean skipLine, String text) {
send(new TextGameEvent(skipLine, text));
}
public void sendImage(String image, String description) {
send(new ImageGameEvent(image, description));
}
public void sendFace(Face face, String description) {
send(new FaceGameEvent(face, description));
}
public void setGroup(Group group) {
if (group == this.group) {
return; }
if (this.group != null) {
this.group.removeGroupListener(this);
}
this.group = group;
group.addGroupListener(this);
refreshGroupMembers(true);
}
private void setArea(Area area) {
if (this.area == area) {
return; }
if (this.area != null) {
this.area.removeAreaListener(this);
}
this.area = area;
send(area.makeMapGameEvent());
area.addAreaListener(this);
}
public void setRoom(Room room) {
if (this.room == room) {
return; }
if (this.room != null) {
this.room.removeRoomListener(this);
}
setArea(room.getArea());
this.room = room;
room.addRoomListener(this);
sendLocationGameEvent();
refreshRoomEntities();
refreshGroupMembers(false);
}
public void recalcTrackingMode() {
boolean tracking = player.getAdminFlags().contains(AdminFlag.TRACKING);
if (tracking == (trackSet != null)) {
return; }
if (tracking) {
trackSet = new HashSet<Player>();
TrackingSystem.addTrackingListener(this);
for (Player p : Player.getOnlinePlayers()) {
trackingChanged(p, p.getRoom());
}
}
else {
TrackingSystem.removeTrackingListener(this);
for (Player p : trackSet) {
send(new TrackGameEvent(p.getId(), null));
}
trackSet = null;
}
}
public void roomImageChanged(String filename) {
sendLocationGameEvent();
}
private void refreshGroupMembers(boolean clear) {
List<PubGroupMember> list = new ArrayList<PubGroupMember>();
groupMemberSet = new HashSet<Player>();
groupMemberSet.add(player);
list.add(player.getPubGroupMember(player));
for (Player p : group.players()) {
if (p != player) {
groupMemberSet.add(p);
list.add(player.getPubGroupMember(p));
}
}
PubGroupMember[] pgm = new PubGroupMember[list.size()];
list.toArray(pgm);
send(new GroupGameEvent(clear ? GroupGameEvent.EventType.CLEAR : GroupGameEvent.EventType.MODIFY, pgm));
}
public void refreshRoomEntities() {
List<PubRoomEntity> list = new ArrayList<PubRoomEntity>();
roomEntitySet = new HashSet<RoomEntity>();
for (RoomEntity entity : room.getRoomEntities()) {
if (entity != player && entity.isVisibleTo(player)) {
roomEntitySet.add(entity);
list.add(player.getPubRoomEntity(entity));
}
}
PubRoomEntity[] pre = new PubRoomEntity[list.size()];
list.toArray(pre);
send(new RoomEntityGameEvent(RoomEntityGameEvent.EventType.CLEAR, pre));
}
public void roomEntityAdded(RoomEntity entity) {
if (entity == player) {
return; }
if (entity.isVisibleTo(player)) {
if (roomEntitySet.add(entity)) {
send(new RoomEntityGameEvent(RoomEntityGameEvent.EventType.ADD, player.getPubRoomEntity(entity)));
}
}
}
public void roomEntityRemoved(RoomEntity entity) {
if (entity == player) {
return; }
if (roomEntitySet.remove(entity)) {
send(new RoomEntityGameEvent(RoomEntityGameEvent.EventType.REMOVE, player.getPubRoomEntity(entity)));
}
}
public void roomEntityChanged(RoomEntity entity, TokenString msg) {
if (entity == player) {
return; }
boolean visibleNow = entity.isVisibleTo(player);
boolean visibleBefore = roomEntitySet.contains(entity);
if (visibleNow || visibleBefore) {
if (msg != null) {
player.sendText(true, null, null, null, msg, null, entity);
}
if (visibleNow && visibleBefore) {
send(new RoomEntityGameEvent(RoomEntityGameEvent.EventType.MODIFY, player.getPubRoomEntity(entity)));
}
else if (visibleNow) {
roomEntitySet.add(entity);
send(new RoomEntityGameEvent(RoomEntityGameEvent.EventType.ADD, player.getPubRoomEntity(entity)));
}
else {
roomEntitySet.remove(entity);
send(new RoomEntityGameEvent(RoomEntityGameEvent.EventType.REMOVE, player.getPubRoomEntity(entity)));
}
}
}
public void trackingChanged(Player target, Room theirRoom) {
if (target == player) {
return; }
if (theirRoom != null && theirRoom.getArea() == area) {
trackSet.add(target);
send(new TrackGameEvent(target.getId(), new Point(theirRoom.getX(), theirRoom.getY())));
}
else if (trackSet.remove(target)) {
send(new TrackGameEvent(target.getId(), null));
}
}
public void process(byte[] b) throws Exception {
ClientEvent e = ClientEvent.forByteArray(b);
ClientEventType type = e.getClientEventType();
switch (type) {
case COMMAND: {
CommandClientEvent evt = (CommandClientEvent) e;
CommandProcessor.process(player, evt.getCommand());
return;
}
case EDIT_ROOM: {
EditRoomClientEvent evt = (EditRoomClientEvent) e;
RoomCommand.notifyRoomEditComplete(player, evt);
return;
}
case FACE_CHOOSER: {
FaceChooserClientEvent evt = (FaceChooserClientEvent) e;
player.setFace(evt.getFace());
sendText(true, "Your face choices have been saved.");
return;
}
case MAIL_CREATE: {
MailCreateClientEvent evt = (MailCreateClientEvent) e;
MailCommand.notifyMailCreateComplete(player, evt);
return;
}
case CONSOLE_COMMAND: {
ExecuteManualScriptClientEvent evt = (ExecuteManualScriptClientEvent) e;
runScriptingCommand(evt);
return;
}
case CONSOLE_SAVE: {
SaveScriptClientEvent evt = (SaveScriptClientEvent) e;
saveScript(evt);
return;
}
case IMAGE_UPLOAD: {
ImageUploadClientEvent evt = (ImageUploadClientEvent) e;
uploadImage(evt);
return;
}
default: throw new FatalError("Unrecognized client event type: " + type);
}
}
private void uploadImage(ImageUploadClientEvent evt) {
final NPC npc = IdType.getNPCById(evt.getNPCId());
if (npc == null) {
player.sendText(true, "That NPC no longer exists.");
return;
}
final String[] filename = new String[] { evt.getFilename() };
Utils.uploadData(player, filename, evt.getUploadData(), new Runnable() {
public void run() {
if (npc.isDestroyed()) {
player.sendText(true, "That NPC no longer exists.");
return;
}
npc.setImage(filename[0]);
player.sendText(true, "@10FNPC image has been set to uploaded image.");
}
});
}
private void saveScript(final SaveScriptClientEvent evt) {
if (!player.getAdminFlags().contains(AdminFlag.CODER)) {
send(new SaveScriptResponseGameEvent(evt.getConsoleId(), true, "Console access denied."));
return;
}
if (evt.getScriptType() == null) {
send(new SaveScriptResponseGameEvent(evt.getConsoleId(), true, "Invalid script type."));
}
String filename = evt.getFilename().trim().toLowerCase();
if (filename == null || filename.length() == 0) {
send(new SaveScriptResponseGameEvent(evt.getConsoleId(), true, "Please enter a filename."));
return;
}
for (int i = 0; i < filename.length(); i++) {
if (ScriptingSystem.VALID_FILENAME_CHARS.indexOf(filename.charAt(i)) < 0) {
send(new SaveScriptResponseGameEvent(evt.getConsoleId(), true, "Invalid filename. Only [A-Za-z0-9._] allowed."));
return;
}
}
ScriptingSystem.saveScript(evt.getScriptType(), filename, ScriptingLanguage.GROOVY, evt.getScript());
send(new SaveScriptResponseGameEvent(evt.getConsoleId(), false, "Script saved."));
}
private void runScriptingCommand(final ExecuteManualScriptClientEvent evt) {
if (!player.getAdminFlags().contains(AdminFlag.CODER)) {
sendText(true, "Console access denied.");
return;
}
Script script = new Script(evt.getScriptType(), "", ScriptingLanguage.GROOVY, evt.getCommand());
String response = script.run(new HashMap<String,Object>());
send(new ExecuteManualScriptResponseGameEvent(evt.getConsoleId(), response));
}
private void sendLocationGameEvent() {
send(new LocationGameEvent(new Point(room.getX(), room.getY()), room.getImage()));
}
public void groupMemberAdded(Player p) {
if (groupMemberSet.add(p)) {
send(new GroupGameEvent(GroupGameEvent.EventType.ADD, player.getPubGroupMember(p)));
}
}
public void groupMemberChanged(Player p) {
if (groupMemberSet.contains(p)) {
send(new GroupGameEvent(GroupGameEvent.EventType.MODIFY, player.getPubGroupMember(p)));
}
}
public void groupMemberRemoved(Player p) {
if (groupMemberSet.remove(p)) {
send(new GroupGameEvent(GroupGameEvent.EventType.REMOVE, player.getPubGroupMember(p)));
}
}
public void roomChanged(PubRoom newRoom) {
send(new RoomUpdatedGameEvent(newRoom));
}
public void roomDeleted(Point p) {
send(new RoomDestroyedGameEvent(p));
}
public void launchRoomEditor(Room roomToEdit, boolean isAdmin) {
send(new RoomEditorGameEvent(
roomToEdit.makePubRoom(),
roomToEdit.getDescription(null),
player.getUploadDirectory(),
roomToEdit.getImage(),
isAdmin));
}
public void launchTextEditor(TextEditorType type, int id, String text) {
send(new TextEditorGameEvent(type, id, text));
}
public void launchFaceChooser(Face face) {
send(new FaceChooserGameEvent(face));
}
public void launchConsole(ScriptType type, String filename, String text) {
send(new ScriptEditorGameEvent(type, filename, text));
}
public void refreshEntity(RoomEntity entity) {
if (roomEntitySet.contains(entity)) {
roomEntityChanged(entity, null);
}
if (!(entity instanceof Player)) {
return;
}
Player p = (Player) entity;
if (groupMemberSet.contains(p)) {
groupMemberChanged(p);
}
}
public void recalcCommandLineColor() {
Color fg, bg;
boolean afk = player.isAFK();
boolean invis = player.isAdminInvisible();
if (afk) {
fg = Color.white;
bg = Color.red;
}
else if (invis) {
fg = Color.black;
bg = Color.yellow;
}
else {
fg = null;
bg = null;
}
send(new CommandLineColorGameEvent(fg, bg));
}
public void sendTable(String[][] data, String[] headers, int[] sizes) {
send(new TableGameEvent(data, headers, sizes));
}
public void roomEntityHealth(RoomEntity entity, long hp, long maxHP) {
if (roomEntitySet.contains(entity)) {
send(new StatGameEvent(StatGameEvent.TableType.ROOM_ENTITY_TABLE, entity.getId(), StatGameEvent.StatType.HEALTH, hp, maxHP));
}
}
public void groupMemberHealth(Player target, long hp, long maxHP) {
if (groupMemberSet.contains(target)) {
send(new StatGameEvent(StatGameEvent.TableType.GROUP_MEMBER_TABLE, target.getId(), StatGameEvent.StatType.HEALTH, hp, maxHP));
}
}
void send(ByteSource bs) {
client.send(bs);
}
public void clearScreen() {
send(new ClearScreenGameEvent());
}
public void doImageUpload(int npcId) {
send(new ImageUploadGameEvent(npcId));
}
}