diff --git a/scripts/commands/addfriend.py b/scripts/commands/addfriend.py new file mode 100644 index 00000000..dbbd0859 --- /dev/null +++ b/scripts/commands/addfriend.py @@ -0,0 +1,10 @@ +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + actorGhost = actor.getSlottedObject("ghost") + chatSrvc = core.chatService + chatSrvc.addFriend(actorGhost, commandString, 1) + return \ No newline at end of file diff --git a/scripts/commands/getfriendlist.py b/scripts/commands/getfriendlist.py new file mode 100644 index 00000000..0a478a48 --- /dev/null +++ b/scripts/commands/getfriendlist.py @@ -0,0 +1,11 @@ +from protocol.swg import ChatOnGetFriendsList +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + ghost = actor.getSlottedObject('ghost') + friendsListMessage = ChatOnGetFriendsList(ghost) + actor.getClient().getSession().write(friendsListMessage.serialize()) + return \ No newline at end of file diff --git a/scripts/commands/removefriend.py b/scripts/commands/removefriend.py new file mode 100644 index 00000000..886ed2d8 --- /dev/null +++ b/scripts/commands/removefriend.py @@ -0,0 +1,10 @@ +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + ghost = actor.getSlottedObject('ghost') + chatSvc = core.chatService + chatSvc.removeFriend(ghost, commandString, 1) + return \ No newline at end of file diff --git a/src/protocol/swg/ChatFriendsListUpdate.java b/src/protocol/swg/ChatFriendsListUpdate.java new file mode 100644 index 00000000..ec4ead39 --- /dev/null +++ b/src/protocol/swg/ChatFriendsListUpdate.java @@ -0,0 +1,53 @@ +package protocol.swg; + +import java.nio.ByteOrder; + +import main.NGECore; + +import org.apache.mina.core.buffer.IoBuffer; + +import engine.resources.config.Config; +import engine.resources.config.DefaultConfig; + +public class ChatFriendsListUpdate extends SWGMessage { + + + private NGECore core; + private String friendName; + private byte onlineFlag; + + // FriendStatusChangeMessage + // online/offline messages + public ChatFriendsListUpdate(String friendName, byte onlineFlag) { + this.friendName = friendName; + this.onlineFlag = onlineFlag; + } + + @Override + public void deserialize(IoBuffer data) { + + + } + + @Override + public IoBuffer serialize() { + Config config = new Config(); + config.setFilePath("nge.cfg"); + if (!(config.loadConfigFile())) { + config = DefaultConfig.getConfig(); + } + String serverName = config.getString("GALAXY_NAME"); + + IoBuffer result = IoBuffer.allocate(50 + friendName.length()).order(ByteOrder.LITTLE_ENDIAN); + result.putShort((short) 3); + result.putInt(0x6CD2FCD8); + result.put(getAsciiString("SWG")); + result.put(getAsciiString(serverName)); + result.put(getAsciiString(friendName)); + result.put(onlineFlag); + result.flip(); + //System.out.println("ChatFriendsListUpdate: " + result.getHexDump()); + return result; + } + +} diff --git a/src/protocol/swg/ChatOnAddFriend.java b/src/protocol/swg/ChatOnAddFriend.java new file mode 100644 index 00000000..be805945 --- /dev/null +++ b/src/protocol/swg/ChatOnAddFriend.java @@ -0,0 +1,25 @@ +package protocol.swg; + +import java.nio.ByteOrder; + +import org.apache.mina.core.buffer.IoBuffer; + +public class ChatOnAddFriend extends SWGMessage { + + @Override + public void deserialize(IoBuffer data) { + + } + + @Override + public IoBuffer serialize() { + IoBuffer result = IoBuffer.allocate(20).order(ByteOrder.LITTLE_ENDIAN); + + result.putShort((short) 0x03); + result.putInt(0x2B2A0D94); + result.putLong(0); + result.flip(); + return result; + } + +} diff --git a/src/protocol/swg/ChatOnChangeFriendStatus.java b/src/protocol/swg/ChatOnChangeFriendStatus.java new file mode 100644 index 00000000..acfa6759 --- /dev/null +++ b/src/protocol/swg/ChatOnChangeFriendStatus.java @@ -0,0 +1,56 @@ +package protocol.swg; + +import java.nio.ByteOrder; + +import main.NGECore; + +import org.apache.mina.core.buffer.IoBuffer; + +import engine.resources.config.Config; +import engine.resources.config.DefaultConfig; +import resources.objects.player.PlayerObject; + + +public class ChatOnChangeFriendStatus extends SWGMessage { + + private NGECore core; + private String name; + private int type; + private long playerID; + + // Adds or Removes a friend, not to be confused with online/offline status + public ChatOnChangeFriendStatus(long playerID, String name, int type) { + this.name = name; + this.type = type; + this.playerID = playerID; + } + @Override + public void deserialize(IoBuffer data) { + + } + + @Override + public IoBuffer serialize() { + Config config = new Config(); + config.setFilePath("nge.cfg"); + if (!(config.loadConfigFile())) { + config = DefaultConfig.getConfig(); + } + String server = config.getString("GALAXY_NAME"); + IoBuffer result = IoBuffer.allocate(100 + this.name.length() + server.length()).order(ByteOrder.LITTLE_ENDIAN); + result.putShort((short)0x06); + result.putInt(0x54336726); + result.putLong(playerID); + + result.put(getAsciiString("SWG")); + result.put(getAsciiString(server)); + result.put(getAsciiString(name)); + result.putInt(0); + result.putInt(type); + result.put((byte) 0); + result.flip(); + //System.out.println("ChatOnChangeFriendStatus: " + result.getHexDump()); + return result; + } + +} diff --git a/src/protocol/swg/ChatOnGetFriendsList.java b/src/protocol/swg/ChatOnGetFriendsList.java new file mode 100644 index 00000000..6ee5fea6 --- /dev/null +++ b/src/protocol/swg/ChatOnGetFriendsList.java @@ -0,0 +1,63 @@ +package protocol.swg; + +import java.nio.ByteOrder; +import java.util.List; + +import org.apache.mina.core.buffer.IoBuffer; + +import engine.resources.config.Config; +import engine.resources.config.DefaultConfig; +import resources.objects.player.PlayerObject; + +public class ChatOnGetFriendsList extends SWGMessage { + + private List friendList; + private PlayerObject player; + private long characterId; + private int listSize; + + public ChatOnGetFriendsList(PlayerObject player) { + this.characterId = player.getObjectID(); + this.friendList = player.getFriendList(); + this.listSize = friendList.size(); + this.player = player; + } + @Override + public void deserialize(IoBuffer data) { + // XXX Auto-generated method stub + + } + + @Override + public IoBuffer serialize() { + Config config = new Config(); + config.setFilePath("nge.cfg"); + if (!(config.loadConfigFile())) { + config = DefaultConfig.getConfig(); + } + + IoBuffer buffer = IoBuffer.allocate(200).order(ByteOrder.LITTLE_ENDIAN); + + buffer.putShort((short) 3); + buffer.putInt(0xE97AB594); + buffer.putLong(characterId); + buffer.putInt(listSize); // friends list size + + + for (String friend : player.getFriendList()) { + + buffer.put(getAsciiString("SWG")); + buffer.put(getAsciiString(config.getString("GALAXY_NAME"))); + + int friendIndex = friendList.indexOf(friend); + + buffer.put(getAsciiString(friendList.get(friendIndex))); + } + int size = buffer.position(); + buffer = IoBuffer.allocate(size).put(buffer.array(), 0, size); + buffer.flip(); + //System.out.println("ChatOnGetFriendsList: " + buffer.getHexDump()); + return buffer; + } + +} diff --git a/src/resources/objects/player/PlayerMessageBuilder.java b/src/resources/objects/player/PlayerMessageBuilder.java index 82761dcc..e121fbf3 100644 --- a/src/resources/objects/player/PlayerMessageBuilder.java +++ b/src/resources/objects/player/PlayerMessageBuilder.java @@ -22,12 +22,12 @@ package resources.objects.player; import java.nio.ByteOrder; +import java.util.List; import java.util.Map.Entry; import org.apache.mina.core.buffer.IoBuffer; import resources.objects.ObjectMessageBuilder; - import resources.objects.waypoint.WaypointObject; public class PlayerMessageBuilder extends ObjectMessageBuilder { @@ -508,6 +508,43 @@ public class PlayerMessageBuilder extends ObjectMessageBuilder { } + public IoBuffer buildFriendAddDelta(String friend) { + + IoBuffer buffer = bufferPool.allocate(100, false).order(ByteOrder.LITTLE_ENDIAN); + PlayerObject player = (PlayerObject) object; + + buffer.put((byte) 1); // updateType (SubType) + + buffer.putShort((short) player.getFriendList().indexOf(friend)); + buffer.put(getAsciiString(friend)); + + int size = buffer.position(); + buffer.flip(); + + buffer = createDelta("PLAY", (byte) 9, (short) 1, (short) 7, buffer, size + 4); + + return buffer; + + } + + public IoBuffer buildFriendRemoveDelta(String friend) { + + IoBuffer buffer = bufferPool.allocate(100, false).order(ByteOrder.LITTLE_ENDIAN); + PlayerObject player = (PlayerObject) object; + + buffer.put((byte) 0); // updateType (SubType) + + buffer.putShort((short) player.getFriendList().indexOf(friend)); + + int size = buffer.position(); + buffer.flip(); + + buffer = createDelta("PLAY", (byte) 9, (short) 1, (short) 7, buffer, size + 4); + + return buffer; + + } + public int getProfData(String profession) { switch (profession) { diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index 00d644fb..6f732952 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import resources.objects.SWGList; import resources.objects.waypoint.WaypointObject; import com.sleepycat.persist.model.NotPersistent; @@ -374,6 +375,18 @@ public class PlayerObject extends SWGObject { } } + public void friendAdd(String friend) { + synchronized(objectMutex) { + getContainer().getClient().getSession().write(messageBuilder.buildFriendAddDelta(friend)); + } + } + + public void friendRemove(String friend) { + synchronized(objectMutex) { + getContainer().getClient().getSession().write(messageBuilder.buildFriendRemoveDelta(friend)); + } + } + public List getIgnoreList() { return ignoreList; } diff --git a/src/services/SimulationService.java b/src/services/SimulationService.java index 5c5a77eb..eaccd7c6 100644 --- a/src/services/SimulationService.java +++ b/src/services/SimulationService.java @@ -52,6 +52,9 @@ import engine.resources.scene.quadtree.QuadTree; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; +import protocol.swg.ChatFriendsListUpdate; +import protocol.swg.ChatOnChangeFriendStatus; +import protocol.swg.ChatOnGetFriendsList; import protocol.swg.CmdStartScene; import protocol.swg.HeartBeatMessage; import protocol.swg.ObjControllerMessage; @@ -64,6 +67,7 @@ import protocol.swg.objectControllerObjects.TargetUpdate; import resources.objects.cell.CellObject; import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; import resources.common.*; import toxi.geom.Line3D; import toxi.geom.Ray3D; @@ -129,6 +133,9 @@ public class SimulationService implements INetworkDispatch { core.commandService.registerCommand("waypoint"); core.commandService.registerCommand("setwaypointactivestatus"); core.commandService.registerCommand("setwaypointname"); + core.commandService.registerCommand("addfriend"); + core.commandService.registerCommand("removefriend"); + core.commandService.registerCommand("getfriendlist"); } public void add(SWGObject object, int x, int y) { @@ -437,8 +444,23 @@ public class SimulationService implements INetworkDispatch { if(client.getParent() == null) return; - + CreatureObject object = (CreatureObject) client.getParent(); + PlayerObject ghost = (PlayerObject) object.getSlottedObject("ghost"); + + if (ghost != null) { + String objectShortName = object.getCustomName(); + + if (object.getCustomName().contains(" ")) { + String[] splitName = object.getCustomName().toLowerCase().split(" "); + objectShortName = splitName[0]; + } + + core.chatService.playerStatusChange(objectShortName, (byte) 0); + } + + session.suspendWrite(); + boolean remove = remove(object, object.getPosition().x, object.getPosition().z); if(remove) System.out.println("Successful quadtree remove"); @@ -467,6 +489,7 @@ public class SimulationService implements INetworkDispatch { public void handleZoneIn(Client client) { + if(client.getParent() == null) return; @@ -488,7 +511,36 @@ public class SimulationService implements INetworkDispatch { if(object.getParentId() == 0) add(object, pos.x, pos.z); - + + PlayerObject ghost = (PlayerObject) object.getSlottedObject("ghost"); + if (ghost != null) { + + String objectShortName = object.getCustomName().toLowerCase(); + + if (object.getCustomName().contains(" ")) { + String[] splitName = object.getCustomName().toLowerCase().split(" "); + objectShortName = splitName[0].toLowerCase(); + } + + core.chatService.playerStatusChange(objectShortName, (byte) 1); + + if (ghost.getFriendList().isEmpty() == false) { + // Find out what friends are online/offline + for (String friend : ghost.getFriendList()) { + SWGObject friendObject = (SWGObject) core.chatService.getObjectByFirstName(friend); + if (friendObject == null) + continue; + if(friendObject.isInQuadtree() == true) { + ChatFriendsListUpdate onlineNotifyStatus = new ChatFriendsListUpdate(friend, (byte) 1); + client.getSession().write(onlineNotifyStatus.serialize()); + + } else { + ChatOnChangeFriendStatus changeStatus = new ChatOnChangeFriendStatus(object.getObjectID(), friend, 0); + client.getSession().write(changeStatus.serialize()); + } + } + } + } } public void transferToPlanet(SWGObject object, Planet planet, Point3D newPos, Quaternion newOrientation) { diff --git a/src/services/chat/ChatService.java b/src/services/chat/ChatService.java index fecb6110..faa920a8 100644 --- a/src/services/chat/ChatService.java +++ b/src/services/chat/ChatService.java @@ -22,8 +22,12 @@ package services.chat; import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; @@ -43,16 +47,22 @@ import engine.resources.scene.Point3D; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; import resources.common.*; - +import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; +import protocol.swg.ChatOnChangeFriendStatus; import protocol.swg.ChatDeletePersistentMessage; +import protocol.swg.ChatFriendsListUpdate; import protocol.swg.ChatInstantMessageToCharacter; import protocol.swg.ChatInstantMessagetoClient; +import protocol.swg.ChatOnAddFriend; +import protocol.swg.ChatOnGetFriendsList; import protocol.swg.ChatOnSendInstantMessage; import protocol.swg.ChatOnSendPersistentMessage; import protocol.swg.ChatPersistentMessageToClient; import protocol.swg.ChatPersistentMessageToServer; import protocol.swg.ChatRequestPersistentMessage; import protocol.swg.ObjControllerMessage; +import protocol.swg.clientSecureTradeManager.AddItemMessage; import protocol.swg.objectControllerObjects.PlayerEmote; import protocol.swg.objectControllerObjects.SpatialChat; import main.NGECore; @@ -307,7 +317,113 @@ public class ChatService implements INetworkDispatch { } }); + } + + public void playerStatusChange(String name, byte status) { + + String shortName = name.toLowerCase(); + ConcurrentHashMap clients = core.getActiveConnectionsMap(); + + for(Client client : clients.values()) { + + + if(client.getParent() == null) + continue; + + PlayerObject clientGhost = (PlayerObject) client.getParent().getSlottedObject("ghost"); + + if(clientGhost == null) + continue; + + if (clientGhost.getFriendList().contains(shortName)) { + // online/offline message + ChatFriendsListUpdate updateNotifyStatus = new ChatFriendsListUpdate(name, (byte) status); + client.getSession().write(updateNotifyStatus.serialize()); + + } + + } + } + + public void removeFriend(PlayerObject actor, String friendName, boolean notify) { + + friendName = friendName.toLowerCase(); + + SWGObject friendObject = getObjectByFirstName(friendName); + + if (actor == null || actor.getContainer() == null) + return; + + List friendList = actor.getFriendList(); + + if (notify) { + ChatOnChangeFriendStatus removeMessage = new ChatOnChangeFriendStatus(actor.getContainer().getObjectID(), friendName, 0); + actor.getContainer().getClient().getSession().write(removeMessage.serialize()); + + if(friendList.contains(friendName)) { + actor.friendRemove(friendName); + //friendList.remove(friendName); + } + } + } + + public void addFriend(PlayerObject actor, String friendName, boolean notify) { + // ChatOnAddFriend, ChatOnChangeFriendStatus, ChatFriendsListUpdate, ChatSystemMessage, ChatOnGetFriendsList + SWGObject friendObject = getObjectByFirstName(friendName); + + PlayerObject friend = (PlayerObject) friendObject.getSlottedObject("ghost"); + CreatureObject friendCreature = (CreatureObject) friend.getContainer(); + List friendList = actor.getFriendList(); + + if (actor == null || friendObject.getSlottedObject("ghost") == null) + return; + + CreatureObject actorCreature = (CreatureObject) actor.getContainer(); + if (actorCreature == null) + return; + + boolean friendIsOnline = friendObject.isInQuadtree(); + + String friendShortName = friendCreature.getCustomName().toLowerCase(); + + if (friendCreature.getCustomName().contains(" ")) { + String[] splitName = friendCreature.getCustomName().toLowerCase().split(" "); + friendShortName = splitName[0]; + } + + if (friendList.contains(friendShortName)) { + actorCreature.sendSystemMessage(friendShortName + " is already on your friends list.", (byte) 0); + return; + } + + + + if (notify) { + ChatOnAddFriend init = new ChatOnAddFriend(); + actorCreature.getClient().getSession().write(init.serialize()); + + ChatOnChangeFriendStatus addMessage = new ChatOnChangeFriendStatus(actorCreature.getObjectID(), friendShortName, 1); + actorCreature.getClient().getSession().write(addMessage.serialize()); + + if(friendIsOnline) { + ChatFriendsListUpdate updateStatus = new ChatFriendsListUpdate(friendShortName, (byte)1); + actorCreature.getClient().getSession().write(updateStatus.serialize()); + } + } + + friendList.add(friendShortName); + + //actor.friendAdd(friendShortName); + //actor.friendAdd(friendShortName); + //if (actor.getFriendList().size() != 0) { + //ChatOnGetFriendsList sendFriendsList = new ChatOnGetFriendsList(actor); + //actorCreature.getClient().getSession().write(sendFriendsList.serialize()); + //} + + actorCreature.sendSystemMessage(friendShortName + " has been added to your friends list.", (byte) 0); + + //actor.friendAdd(friendShortName); } public void sendPersistentMessageHeader(Client client, Mail mail) { diff --git a/src/services/object/ObjectService.java b/src/services/object/ObjectService.java index 1a8d7560..0810e682 100644 --- a/src/services/object/ObjectService.java +++ b/src/services/object/ObjectService.java @@ -45,6 +45,7 @@ import org.python.core.PyObject; import com.sleepycat.persist.EntityCursor; +import protocol.swg.ChatOnGetFriendsList; import protocol.swg.CmdSceneReady; import protocol.swg.CmdStartScene; import protocol.swg.HeartBeatMessage; @@ -429,6 +430,11 @@ public class ObjectService implements INetworkDispatch { CmdSceneReady sceneReady = new CmdSceneReady(); client.getSession().write(sceneReady.serialize()); + PlayerObject ghost = (PlayerObject) creature.getSlottedObject("ghost"); + + ChatOnGetFriendsList friendsListMessage = new ChatOnGetFriendsList(ghost); + client.getSession().write(friendsListMessage.serialize()); + core.playerService.postZoneIn(creature); }