diff --git a/scripts/commands/auction.py b/scripts/commands/auction.py new file mode 100644 index 00000000..dc63c8bc --- /dev/null +++ b/scripts/commands/auction.py @@ -0,0 +1,10 @@ +from main import NGECore +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + room = core.chatService.getChatRoomByAddress('SWG.' + NGECore.getInstance().getGalaxyName() + '.' + 'Auction') + core.chatService.sendChatRoomMessage(actor, room.getRoomId(), 0, commandString) + return \ No newline at end of file diff --git a/scripts/commands/groupchat.py b/scripts/commands/groupchat.py new file mode 100644 index 00000000..cce1a9ff --- /dev/null +++ b/scripts/commands/groupchat.py @@ -0,0 +1,20 @@ +from main import NGECore +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + + group = core.objectService.getObject(actor.getGroupId()) + + if group is None: + return + + room = core.chatService.getChatRoom(group.getChatRoomId()) + + if room is None: + return + + core.chatService.sendChatRoomMessage(actor, room.getRoomId(), 0, commandString) + return \ No newline at end of file diff --git a/scripts/commands/planetchat.py b/scripts/commands/planetchat.py new file mode 100644 index 00000000..04a9de62 --- /dev/null +++ b/scripts/commands/planetchat.py @@ -0,0 +1,14 @@ +from main import NGECore +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + room = core.chatService.getChatRoomByAddress('SWG.' + core.getGalaxyName() + '.' + actor.getPlanet().getName() + '.Planet') + + if room is None: + return + + core.chatService.sendChatRoomMessage(actor, room.getRoomId(), 0, commandString) + return \ No newline at end of file diff --git a/src/main/NGECore.java b/src/main/NGECore.java index d64b5031..08e9847c 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -421,6 +421,9 @@ public class NGECore { //terrainService.addPlanet(46, "kaas", "terrain/kaas.trn", true); //end terrainList + + chatService.loadChatRooms(); + spawnService = new SpawnService(this); terrainService.loadClientPois(); // Travel Points @@ -462,7 +465,6 @@ public class NGECore { instanceService = new InstanceService(this); zoneDispatch.addService(instanceService); - //travelService.startShuttleSchedule(); weatherService = new WeatherService(this); weatherService.loadPlanetSettings(); diff --git a/src/protocol/swg/chat/ChatOnEnteredRoom.java b/src/protocol/swg/chat/ChatOnEnteredRoom.java index 786f16a6..fe82b29c 100644 --- a/src/protocol/swg/chat/ChatOnEnteredRoom.java +++ b/src/protocol/swg/chat/ChatOnEnteredRoom.java @@ -42,6 +42,7 @@ public class ChatOnEnteredRoom extends SWGMessage { this.characterName = characterName; this.success = success; this.roomId = roomId; + this.join = join; } @Override @@ -60,7 +61,7 @@ public class ChatOnEnteredRoom extends SWGMessage { buffer.putInt(CRC.StringtoCRC("ChatOnLeaveRoom")); buffer.put(getAsciiString("SWG")); buffer.put(getAsciiString(galaxy)); - buffer.put(getAsciiString(characterName)); + buffer.put(getAsciiString(characterName.toLowerCase())); buffer.putInt(success); buffer.putInt(roomId); buffer.putInt(0); diff --git a/src/protocol/swg/chat/ChatSendToRoom.java b/src/protocol/swg/chat/ChatSendToRoom.java index 0a751ac0..1c603c3c 100644 --- a/src/protocol/swg/chat/ChatSendToRoom.java +++ b/src/protocol/swg/chat/ChatSendToRoom.java @@ -21,9 +21,13 @@ ******************************************************************************/ package protocol.swg.chat; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; + import org.apache.mina.core.buffer.IoBuffer; import protocol.swg.SWGMessage; +import resources.common.StringUtilities; public class ChatSendToRoom extends SWGMessage { @@ -35,10 +39,18 @@ public class ChatSendToRoom extends SWGMessage { @Override public void deserialize(IoBuffer data) { - setMessage(getUnicodeString(data)); - data.getInt(); - setRoomId(data.getInt()); - setMsgId(data.getInt()); + try { + data.getShort(); + data.getInt(); + int size = data.getInt(); + message = new String(ByteBuffer.allocate(size * 2).put(data.array(), data.position(), size * 2).array(), "UTF-16LE"); + data.position(data.position() + size * 2); + data.getInt(); + setRoomId(data.getInt()); + setMsgId(data.getInt()); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } } @Override diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index e24c50ae..d781ed7f 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -1690,7 +1690,12 @@ public class CreatureObject extends TangibleObject implements IPersistent { synchronized(objectMutex) { this.inStealth = inStealth; } + + if (getClient() != null && getClient().getSession() != null) { + getClient().getSession().write(messageBuilder.buildStealthFlagDelta(inStealth)); + } notifyObservers(messageBuilder.buildStealthFlagDelta(inStealth), false); + } public boolean isRadarVisible() { diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index 7c956d6f..853e1d9a 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -44,7 +44,7 @@ import engine.resources.scene.Planet; import engine.resources.scene.Point3D; import engine.resources.scene.Quaternion; -@Persistent(version=11) +@Persistent(version=12) public class PlayerObject extends IntangibleObject { // PLAY 3 @@ -142,6 +142,8 @@ public class PlayerObject extends IntangibleObject { private byte godLevel = 0; + private List chatChannels = new ArrayList(); + public PlayerObject() { super(); messageBuilder = new PlayerMessageBuilder(this); @@ -864,4 +866,19 @@ public class PlayerObject extends IntangibleObject { } } + public List getJoinedChatChannels() { + return chatChannels; + } + + public void addChannel(int roomId) { + chatChannels.add(roomId); + } + + public void removeChannel(int roomId) { + chatChannels.remove(roomId); + } + + public boolean isMemberOfChannel(int roomId) { + return chatChannels.contains(roomId); + } } diff --git a/src/services/GroupService.java b/src/services/GroupService.java index ba159d69..fa4d9d5b 100644 --- a/src/services/GroupService.java +++ b/src/services/GroupService.java @@ -28,6 +28,7 @@ import java.util.Map; import resources.objects.Buff; import resources.objects.creature.CreatureObject; import resources.objects.group.GroupObject; +import services.chat.ChatRoom; import main.NGECore; import engine.clients.Client; import engine.resources.objects.SWGObject; @@ -152,14 +153,13 @@ public class GroupService implements INetworkDispatch { addGroupBuffsToMember(group, leader); addGroupBuffsToMember(group, invited); - //ChatRoom groupChat = core.chatService.createChatRoom("GroupChat", "group." + group.getObjectID(), leader.getCustomName(), true); - //group.setChatRoomId(groupChat.getRoomId()); - - //core.chatService.joinChatRoom(leader, groupChat.getRoomId()); - //core.chatService.joinChatRoom(invited, groupChat.getRoomId()); - + core.chatService.createChatRoom("", "group." + group.getObjectID(), leader.getCustomName(), false); + ChatRoom groupChat = core.chatService.createChatRoom("", "group." + group.getObjectID() + ".GroupChat", leader.getCustomName(), false); + group.setChatRoomId(groupChat.getRoomId()); + groupChat.setVisible(false); + core.chatService.joinChatRoom(leader.getCustomName(), groupChat.getRoomId()); + core.chatService.joinChatRoom(invited.getCustomName(), groupChat.getRoomId()); return; - } GroupObject group = (GroupObject) core.objectService.getObject(leader.getGroupId()); @@ -171,6 +171,7 @@ public class GroupService implements INetworkDispatch { invited.setGroupId(group.getObjectID()); invited.sendSystemMessage("@group:joined_self", (byte) 0); addGroupBuffsToMember(group, invited); + core.chatService.joinChatRoom(invited.getCustomName(), group.getChatRoomId(), true); } else if(group.getMemberList().size() >= 8) { @@ -233,13 +234,12 @@ public class GroupService implements INetworkDispatch { creature.updateGroupInviteInfo(); creature.setGroupId(0); creature.makeUnaware(group); + core.chatService.leaveChatRoom(creature, group.getChatRoomId()); creature.sendSystemMessage("You have left the group.", (byte) 0); for(SWGObject member : memberList) { - CreatureObject creature2 = (CreatureObject) member; creature2.sendSystemMessage(creature.getCustomName() + " has left the group.", (byte) 0); - } removeGroupBuffs(creature); @@ -258,14 +258,16 @@ public class GroupService implements INetworkDispatch { creature2.setInviteSenderName(""); creature2.updateGroupInviteInfo(); creature2.setGroupId(0); - + creature2.makeUnaware(group); + core.chatService.leaveChatRoom(creature2, group.getChatRoomId()); creature2.sendSystemMessage("The group has been disbanded.", (byte) 0); removeGroupBuffs((CreatureObject) member); } + core.chatService.getChatRooms().remove(group.getChatRoomId()); core.objectService.destroyObject(group.getObjectID()); } } diff --git a/src/services/SimulationService.java b/src/services/SimulationService.java index 36ae2775..ac70aa31 100644 --- a/src/services/SimulationService.java +++ b/src/services/SimulationService.java @@ -83,6 +83,7 @@ import resources.common.collidables.AbstractCollidable; import resources.datatables.PlayerFlags; import resources.datatables.Posture; import services.ai.LairActor; +import services.chat.ChatRoom; import toxi.geom.Line3D; import toxi.geom.Ray3D; import toxi.geom.Vec3D; @@ -837,6 +838,18 @@ public class SimulationService implements INetworkDispatch { if(object.getPosture() == Posture.Dead) core.playerService.sendCloningWindow(object, false); + ChatRoom zoneRoom = core.chatService.getChatRoomByAddress("SWG." + core.getGalaxyName() + "." + object.getPlanet().getName() + ".Planet"); + if (zoneRoom != null) { + String chatName = object.getCustomName().toLowerCase(); + + if (chatName.contains(" ")) + chatName = chatName.split(" ")[0]; + + if (!zoneRoom.getUserList().contains(chatName)) { + core.chatService.joinChatRoom(chatName, zoneRoom.getRoomId(), true); + } + } + } public void transferToPlanet(SWGObject object, Planet planet, Point3D newPos, Quaternion newOrientation, SWGObject newParent) { diff --git a/src/services/TerrainService.java b/src/services/TerrainService.java index ddc66042..c8b3aaf5 100644 --- a/src/services/TerrainService.java +++ b/src/services/TerrainService.java @@ -175,12 +175,6 @@ public class TerrainService { core.mapService.addPlanet(planet); noBuildAreas.put(planet, new ArrayList()); loadClientRegions(planet); - - core.chatService.createChatRoom("", name, "system", true); - core.chatService.createChatRoom("public chat for this planet, cannot create rooms here", name + ".Planet", "SYSTEM", true); - core.chatService.createChatRoom("system messages for this planet, cannot create rooms here", name + ".system", "SYSTEM", true); - - System.out.println("Created chat rooms for " + name); } diff --git a/src/services/chat/ChatRoom.java b/src/services/chat/ChatRoom.java index 1cc7f2a2..10f853c1 100644 --- a/src/services/chat/ChatRoom.java +++ b/src/services/chat/ChatRoom.java @@ -28,7 +28,7 @@ import com.sleepycat.persist.model.Entity; import com.sleepycat.persist.model.NotPersistent; import com.sleepycat.persist.model.PrimaryKey; -@Entity +@Entity(version=1) public class ChatRoom { private String creator; // creator of the room (first name only, lowercase) private String description; // title @@ -44,6 +44,7 @@ public class ChatRoom { private boolean moderatorsOnly; private boolean privateRoom; private boolean visible; + private boolean childrenAllowed; private String owner; // current owner of the room (first name only lowercase) public String getCreator() { @@ -145,4 +146,12 @@ public class ChatRoom { public boolean hasBan(String user) { return banList.contains(user.toLowerCase()); } + + public boolean isChildrenAllowed() { + return childrenAllowed; + } + + public void setChildrenAllowed(boolean childrenAllowed) { + this.childrenAllowed = childrenAllowed; + } } diff --git a/src/services/chat/ChatService.java b/src/services/chat/ChatService.java index 04fefd99..32606e64 100644 --- a/src/services/chat/ChatService.java +++ b/src/services/chat/ChatService.java @@ -24,7 +24,9 @@ package services.chat; import java.nio.ByteOrder; import java.util.Date; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Random; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; @@ -40,6 +42,7 @@ import engine.resources.config.Config; import engine.resources.config.DefaultConfig; import engine.resources.database.ObjectDatabase; import engine.resources.objects.SWGObject; +import engine.resources.scene.Planet; import engine.resources.scene.Point3D; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; @@ -82,17 +85,24 @@ public class ChatService implements INetworkDispatch { this.core = core; core.commandService.registerCommand("spatialchatinternal"); core.commandService.registerCommand("socialinternal"); + core.commandService.registerCommand("addignore"); core.commandService.registerCommand("removeignore"); core.commandService.registerCommand("findfriend"); core.commandService.registerCommand("addfriend"); core.commandService.registerCommand("removefriend"); - //core.commandService.registerCommand("gc"); - //core.commandService.registerAlias("g", "gc"); + + core.commandService.registerAlias("g", "groupchat"); + core.commandService.registerAlias("gc", "groupchat"); + core.commandService.registerAlias("groupsay", "groupchat"); + core.commandService.registerAlias("gsay", "groupchat"); + core.commandService.registerAlias("gtell", "groupchat"); + + core.commandService.registerAlias("planet", "planetchat"); + mailODB = core.getMailODB(); chatRoomsODB = core.getChatRoomODB(); - loadChatRooms(); } public void handleSpatialChat(SWGObject speaker, SWGObject target, String chatMessage, short chatType, short moodId) { @@ -363,7 +373,6 @@ public class ChatService implements INetworkDispatch { ChatRoomList listMessage = new ChatRoomList(chatRooms); client.getSession().write(listMessage.serialize()); - //System.out.println("Sent the room request responses."); } }); @@ -402,7 +411,7 @@ public class ChatService implements INetworkDispatch { }); swgOpcodes.put(Opcodes.ChatQueryRoom, (session, data) -> { - data.order(ByteOrder.LITTLE_ENDIAN); + /*data.order(ByteOrder.LITTLE_ENDIAN); //StringUtilities.printBytes(data.array()); Client client = core.getClient(session); @@ -412,14 +421,11 @@ public class ChatService implements INetworkDispatch { SWGObject obj = client.getParent(); if (obj == null) - return; - - - + return;*/ }); swgOpcodes.put(Opcodes.ChatSendToRoom, (session, data) -> { - //System.out.println("send to room!"); + Client client = core.getClient(session); if(client == null) @@ -430,11 +436,11 @@ public class ChatService implements INetworkDispatch { if (obj == null) return; + data.order(ByteOrder.LITTLE_ENDIAN); + data.position(0); ChatSendToRoom sentPacket = new ChatSendToRoom(); sentPacket.deserialize(data); - - //ChatRoom room = getChatRoom(sentPacket.getRoomId()); - + sendChatRoomMessage((CreatureObject) obj, sentPacket.getRoomId(), sentPacket.getMsgId(), sentPacket.getMessage()); }); @@ -449,6 +455,7 @@ public class ChatService implements INetworkDispatch { if (obj == null) return; + data.order(ByteOrder.LITTLE_ENDIAN); data.position(0); ChatEnterRoomById sentPacket = new ChatEnterRoomById(); @@ -456,8 +463,6 @@ public class ChatService implements INetworkDispatch { joinChatRoom(obj.getCustomName(), sentPacket.getRoomId()); - //System.out.println("Entering room... " + sentPacket.getRoomId()); - }); } @@ -706,7 +711,15 @@ public class ChatService implements INetworkDispatch { return mail; } - + public ChatRoom getChatRoomByAddress(String address) { + + for (Entry entry : chatRooms.entrySet()) { + if (entry.getValue().getRoomAddress().equals(address)) + return entry.getValue(); + } + return null; + } + public ChatRoom getChatRoom(int roomId) { return chatRooms.get(roomId); } @@ -725,35 +738,92 @@ public class ChatService implements INetworkDispatch { core.simulationService.notifyAllClients(new ChatSystemMessage(message, new OutOfBand(), DisplayType.Broadcast).serialize()); } - private void loadChatRooms() { + public void loadChatRooms() { /* + * Group Channel: + * SWG.serverName.group.groupObjectId.GroupChat * Battlefields channel format: * SWG.serverName.battlefield.bfMapName * * TODO: Research other channel address formats */ - createChatRoom("", "SWG", "system", true); - createChatRoom("", "SWG." + core.getGalaxyName(), "system", true); + createChatRoom("", "SWG", "system", false); + createChatRoom("", "SWG." + core.getGalaxyName(), "system", false); + createChatRoom("", "SWG." + core.getGalaxyName() + ".Chat", "system", false); createChatRoom("", "system", "system", true); // galaxy system messages - createChatRoom("", "Auction", "system", true); - //createChatRoom("Bounty Hunter chat for this galaxy", "BountyHunter", "SYSTEM", true); - //createChatRoom("Commando chat for this galaxy", "Commando", "SYSTEM", true); + createChatRoom("", "group", "system", false); + createChatRoom("", "guild", "system", false); + + createChatRoom("", "Auction", "system", true); + createChatRoom("public chat for the whole galaxy, cannot create rooms here", "Galaxy", "system", true); + + createChatRoom("Bounty Hunter chat for this galaxy", "BountyHunter", "system", true); + createChatRoom("Commando chat for this galaxy", "Commando", "system", true); + createChatRoom("Officer chat for this galaxy", "Officer", "system", true); + createChatRoom("Entertainer chat for this galaxy", "Entertainer", "system", true); + createChatRoom("Spy chat for this galaxy", "Spy", "system", true); + createChatRoom("Force Sensitive chat for this galaxy", "ForceSensitive", "system", true); + + createChatRoom("Politician chat for this galaxy", "Politician", "system", true); + //createChatRoom("Pilot chat for this galaxy", "Pilot", "system", true); EntityCursor cursor = chatRoomsODB.getCursor(Integer.class, ChatRoom.class); cursor.forEach(room -> { - chatRooms.put(room.getRoomId(), room); + if (!chatRooms.containsValue(room)) + chatRooms.put(room.getRoomId(), room); }); cursor.close(); + + List planets = core.terrainService.getPlanetList(); + planets.forEach(planet -> { + createChatRoom("", planet.getName(), "system", true, true); + createChatRoom("", planet.getName() + ".Chat", "system", true, false); + createChatRoom("public chat for this planet, cannot create rooms here", planet.getName() + ".Planet", "system", true, false); + createChatRoom("system messages for this planet, cannot create rooms here", planet.getName() + ".system", "system", true, false); + Console.println("Created chat rooms for " + planet.getName()); + }); } + /** + * Creates a new ChatRoom that is not persistent and does not allow children. + * @param roomName Name of the room, description + * @param address Address of the room. Defaults to SWG.serverName + the value of this variable if it does not contain it. + * @param creator Creator of the room. Also set as the owner of the room. + * @param isPublic Determines weather or not the channel will show in the list of channels. + * @return {@link ChatRoom} + */ public ChatRoom createChatRoom(String roomName, String address, String creator, boolean isPublic) { - return createChatRoom(roomName, address, creator, isPublic, false); + return createChatRoom(roomName, address, creator, isPublic, false, false); } - public ChatRoom createChatRoom(String roomName, String address, String creator, boolean isPublic, boolean store) { + + /** + * Creates a new ChatRoom with the given values that is not persistent. + * @param roomName Name of the room, description + * @param address Address of the room. Defaults to SWG.serverName + the value of this variable if it does not contain it. + * @param creator Creator of the room. Also set as the owner of the room. + * @param isPublic Determines weather or not the channel will show in the list of channels. + * @param childrenAllowed Setting this to true allows players to create channels within it. + * @return {@link ChatRoom} + */ + public ChatRoom createChatRoom(String roomName, String address, String creator, boolean isPublic, boolean childrenAllowed) { + return createChatRoom(roomName, address, creator, isPublic, false, childrenAllowed); + } + + /** + * Creates a new ChatRoom with the given values. + * @param roomName Name of the room, description + * @param address Address of the room. Defaults to SWG.serverName + the value of this variable if it does not contain it. + * @param creator Creator of the room. Also set as the owner of the room. + * @param isPublic Determines weather or not the channel will show in the list of channels. + * @param store Setting this to true will make the channel persistent through server restarts. + * @param childrenAllowed Setting this to true allows players to create channels within it. + * @return {@link ChatRooms} + */ + public ChatRoom createChatRoom(String roomName, String address, String creator, boolean isPublic, boolean store, boolean childrenAllowed) { if (creator.contains(" ")) creator = creator.split(" ")[0]; @@ -768,6 +838,7 @@ public class ChatService implements INetworkDispatch { room.setOwner(creator.toLowerCase()); room.setVisible(isPublic); room.setRoomId(generateChatRoomId()); + room.setChildrenAllowed(childrenAllowed); chatRooms.put(room.getRoomId(), room); @@ -776,7 +847,9 @@ public class ChatService implements INetworkDispatch { chatRoomsODB.put(room, Integer.class, ChatRoom.class, txn); txn.commitSync(); } - + + //Console.println("Created room " + address + " with ID " + room.getRoomId()); + return room; } @@ -797,7 +870,6 @@ public class ChatService implements INetworkDispatch { ChatRoom room = getChatRoom(roomId); if (room == null) return false; - if (!room.hasUser(user.toLowerCase())) { room.addUser(user.toLowerCase()); @@ -808,9 +880,13 @@ public class ChatService implements INetworkDispatch { creo.getClient().getSession().write(listMessage.serialize()); } } - ChatOnEnteredRoom enterRoom = new ChatOnEnteredRoom(user, 0, roomId, true); - getObjectByFirstName(user).getClient().getSession().write(enterRoom.serialize()); - //System.out.println("Sent message for room " + room.getRoomAddress()); + ChatOnEnteredRoom enter = new ChatOnEnteredRoom(user, 0, room.getRoomId(), true); + SWGObject player = getObjectByFirstName(user); + + if (player == null || player.getClient() == null || player.getClient().getSession() == null) + return false; + + player.getClient().getSession().write(enter.serialize()); return true; } return false; @@ -819,39 +895,57 @@ public class ChatService implements INetworkDispatch { public boolean joinChatRoom(String user, int roomId) { return joinChatRoom(user, roomId, false); } + public void leaveChatRoom(CreatureObject player, int roomId) { + ChatRoom room = getChatRoom(roomId); + if (room == null) + return; + String playerName = player.getCustomName().toLowerCase(); if (playerName.contains(" ")) playerName = playerName.split(" ")[0]; - ChatRoom room = getChatRoom(roomId); - if (room == null) - return; - - ChatOnEnteredRoom enterRoom = new ChatOnEnteredRoom(playerName, 0, roomId, false); - player.getClient().getSession().write(enterRoom.serialize()); + ChatOnEnteredRoom leaveRoom = new ChatOnEnteredRoom(playerName, 0, roomId, false); + player.getClient().getSession().write(leaveRoom.serialize()); if (room.getUserList().contains(player)) room.getUserList().remove(player); + + room.getUserList().forEach(user -> { + SWGObject roomPlayer = getObjectByFirstName(user); + + if (roomPlayer != null && roomPlayer.getClient() != null && roomPlayer.getClient().getSession() != null) + roomPlayer.getClient().getSession().write(leaveRoom.serialize()); + }); } public void sendChatRoomMessage(CreatureObject sender, int roomId, int msgId, String message) { - String senderName = sender.getCustomName().toLowerCase(); + ChatRoom room = getChatRoom(roomId); + + if (room == null) + return; + + String senderName = sender.getCustomName(); if (senderName.contains(" ")) senderName = senderName.split(" ")[0]; - - ChatRoom room = getChatRoom(roomId); - if (room == null) - return; ChatOnSendRoomMessage onSend = new ChatOnSendRoomMessage(0, msgId); sender.getClient().getSession().write(onSend.serialize()); ChatRoomMessage roomMessage = new ChatRoomMessage(roomId, senderName, message); Vector users = room.getUserList(); + users.forEach(user -> { + SWGObject player = getObjectByFirstName(user); + if (player != null && player.getClient() != null && player.getClient().getSession() != null) { + player.getClient().getSession().write(roomMessage.serialize()); + } + }); + } + + public void handleGroupChat(SWGObject sender, String message) { }