Second iteration of the chat refactor

This commit is contained in:
Obique PSWG
2017-04-04 21:54:10 -05:00
parent b7109309fc
commit 8bc071897c
14 changed files with 991 additions and 995 deletions
+22 -11
View File
@@ -33,6 +33,7 @@ import resources.encodables.ProsePackage;
import resources.player.Player;
public class ChatBroadcastIntent extends Intent {
public static final String TYPE = "ChatBroadcastIntent";
private BroadcastType broadcastType;
@@ -53,26 +54,36 @@ public class ChatBroadcastIntent extends Intent {
this(null, receiver, null, BroadcastType.PERSONAL);
this.prose = prose;
}
public ChatBroadcastIntent(String message, BroadcastType type) {
this(message, null, null, type);
}
public ChatBroadcastIntent(Player receiver, String message) {
this(message, receiver, null, BroadcastType.PERSONAL);
}
public ChatBroadcastIntent(String message) {
this(message, null, null, BroadcastType.GALAXY);
public BroadcastType getBroadcastType() {
return broadcastType;
}
public BroadcastType getBroadcastType() { return broadcastType; }
public Player getBroadcaster() { return broadcaster; }
public Terrain getTerrain() { return terrain; }
public String getMessage() { return message; }
public ProsePackage getProse() { return prose; }
public Player getBroadcaster() {
return broadcaster;
}
public Terrain getTerrain() {
return terrain;
}
public String getMessage() {
return message;
}
public ProsePackage getProse() {
return prose;
}
public enum BroadcastType {
AREA,
PLANET,
GALAXY,
PERSONAL
AREA, PLANET, GALAXY, PERSONAL
}
}
@@ -29,60 +29,52 @@ package network.packets.swg.zone.chat;
import network.packets.swg.SWGPacket;
import resources.chat.ChatAvatar;
import resources.chat.ChatResult;
import java.nio.ByteBuffer;
public class ChatOnEnteredRoom extends SWGPacket {
public static final int CRC = getCrc("ChatOnEnteredRoom");
private ChatAvatar avatar;
private int result;
private ChatResult result;
private int chatRoomId;
private int sequence;
public ChatOnEnteredRoom() {
}
public ChatOnEnteredRoom(ChatAvatar avatar, int chatRoomId, int sequence) {
this.avatar = avatar;
this.chatRoomId = chatRoomId;
this.sequence = sequence;
this(avatar, ChatResult.NONE, chatRoomId, sequence);
}
public ChatOnEnteredRoom(ChatAvatar avatar, int result, int chatRoomId, int sequence) {
public ChatOnEnteredRoom(ChatAvatar avatar, ChatResult result, int chatRoomId, int sequence) {
this.avatar = avatar;
this.result = result;
this.chatRoomId = chatRoomId;
this.sequence = sequence;
}
public ChatOnEnteredRoom(ByteBuffer data) {
decode(data);
}
public void decode(ByteBuffer data) {
if (!super.decode(data, CRC))
return;
avatar = getEncodable(data, ChatAvatar.class);
result = getInt(data);
chatRoomId = getInt(data);
sequence = getInt(data);
avatar = getEncodable(data, ChatAvatar.class);
result = ChatResult.fromInteger(getInt(data));
chatRoomId = getInt(data);
sequence = getInt(data);
}
public ByteBuffer encode() {
ByteBuffer data = ByteBuffer.allocate(avatar.getSize() + 18);
addShort(data, 5);
addInt (data, CRC);
addInt(data, CRC);
addEncodable(data, avatar);
addInt(data, result);
addInt (data, chatRoomId);
addInt (data, sequence);
addInt(data, result.getCode());
addInt(data, chatRoomId);
addInt(data, sequence);
return data;
}
public void setResult(int result) {
this.result = result;
}
}
+60 -99
View File
@@ -26,160 +26,121 @@
******************************************************************************/
package resources.chat;
import network.packets.Packet;
import resources.encodables.CachedEncode;
import resources.encodables.Encodable;
import resources.network.NetBuffer;
import resources.network.NetBufferStream;
import resources.persistable.Persistable;
import resources.player.Player;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.nio.ByteBuffer;
import java.util.Locale;
import services.CoreManager;
/**
* @author Waverunner
*/
public class ChatAvatar implements Encodable, Persistable {
private long networkId;
private final CachedEncode cache;
private Player player;
private String name;
private String game = "SWG";
private String galaxy;
// Instances of the class will hardly ever change, so we can just pre-build the data.
private transient byte[] data;
private transient boolean modified;
private transient int size = 0;
public ChatAvatar() {
modified = true;
data = null;
size = 0;
}
public ChatAvatar(long networkId, String name, String galaxy) {
this();
this.networkId = networkId;
this.name = name;
this.galaxy = galaxy;
this(null, null);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
modified = true;
data = null;
size = 0;
public ChatAvatar(Player player, String name) {
this.cache = new CachedEncode(() -> encodeImpl());
this.player = player;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
modified = true;
this.cache.clearCached();
}
public String getGame() {
return game;
}
public void setGame(String game) {
this.game = game;
modified = true;
}
public String getGalaxy() {
return galaxy;
return getCoreGalaxy();
}
public void setGalaxy(String galaxy) {
this.galaxy = galaxy;
modified = true;
public Player getPlayer() {
return player;
}
public long getNetworkId() {
return networkId;
public void setPlayer(Player player) {
this.player = player;
}
private byte [] encodeImpl() {
NetBuffer buffer = NetBuffer.allocate(getSize());
buffer.addAscii("SWG");
buffer.addAscii(getCoreGalaxy());
buffer.addAscii(name);
return buffer.array();
}
public int getSize() {
if (size == 0)
size = 6 + game.length() + name.length() + galaxy.length();
return size;
return 9 + name.length() + getCoreGalaxy().length();
}
@Override
public byte[] encode() {
if (!modified && data != null)
return data;
ByteBuffer buffer = ByteBuffer.allocate(getSize());
Packet.addAscii(buffer, game);
Packet.addAscii(buffer, galaxy);
Packet.addAscii(buffer, name);
data = buffer.array();
modified = false;
return data;
return cache.encode();
}
@Override
public void decode(ByteBuffer data) {
game = Packet.getAscii(data);
galaxy = Packet.getAscii(data);
name = Packet.getAscii(data).toLowerCase(Locale.US);
public void decode(ByteBuffer bb) {
NetBuffer data = NetBuffer.wrap(bb);
data.getAscii(); // SWG
data.getAscii();
name = data.getAscii().toLowerCase(Locale.US);
}
@Override
public void save(NetBufferStream stream) {
stream.addByte(0);
stream.addAscii(galaxy);
stream.addByte(1);
stream.addAscii(name);
}
@Override
public void read(NetBufferStream stream) {
stream.getByte();
galaxy = stream.getAscii();
byte ver = stream.getByte();
if (ver == 0)
stream.getAscii();
name = stream.getAscii();
}
@Override
public String toString() {
return "ChatAvatar[networkId=" + networkId +
", name='" + name + '\'' + ", game='" + game + '\'' + ", galaxy='" + galaxy + "']";
return String.format("ChatAvatar[name='%s']", name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (getClass() != o.getClass())
if (!(o instanceof ChatAvatar))
return false;
// NetworkId will be 0 if decoded from a packet
if (networkId != 0) return ((ChatAvatar) o).getNetworkId() == networkId;
else return ((ChatAvatar) o).getName().equals(getName());
return ((ChatAvatar) o).getName().equals(getName());
}
@Override
public int hashCode() {
// NetworkId will be 0 if decoded from a packet
int result = (networkId != 0 ? (int) (networkId ^ (networkId >>> 32)) : 0);
result = 31 * result + name.hashCode();
return result;
return name.hashCode();
}
public static ChatAvatar getFromPlayer(Player player) {
return new ChatAvatar(player.getNetworkId(), player.getCharacterName().split(" ")[0].toLowerCase(Locale.US), CoreManager.getGalaxy().getName());
return new ChatAvatar(player, player.getCharacterFirstName().toLowerCase(Locale.US));
}
public static ChatAvatar getSystemAvatar(String galaxy) {
return new ChatAvatar(-1, "system", galaxy);
public static ChatAvatar getSystemAvatar() {
return new ChatAvatar(null, "system");
}
private static String getCoreGalaxy() {
return CoreManager.getGalaxy().getName();
}
}
+34 -24
View File
@@ -27,41 +27,51 @@
package resources.chat;
import java.util.HashMap;
import java.util.Map;
/**
* @author Waverunner
*/
public enum ChatResult {
NONE(-1), // The client will just display an "unknown error code" if this is used.
SUCCESS(0),
TARGET_AVATAR_DOESNT_EXIST(4),
ROOM_INVALID_ID(5),
ROOM_INVALID_NAME(6),
CUSTOM_FAILURE(9),
ROOM_AVATAR_BANNED(12),
ROOM_PRIVATE(13),
ROOM_AVATAR_NO_PERMISSION(16),
IGNORED(23),
ROOM_ALREADY_EXISTS(24),
ROOM_ALREADY_JOINED(36),
CHAT_SERVER_UNAVAILABLE(1000000),
ROOM_DIFFERENT_FACTION(1000001),
ROOM_NOT_GCW_DEFENDER_FACTION(1000005);
NONE (-1), // The client will just display an "unknown error code" if this is used.
SUCCESS (0),
TARGET_AVATAR_DOESNT_EXIST (4),
ROOM_INVALID_ID (5),
ROOM_INVALID_NAME (6),
CUSTOM_FAILURE (9),
ROOM_AVATAR_BANNED (12),
ROOM_PRIVATE (13),
ROOM_AVATAR_NO_PERMISSION (16),
IGNORED (23),
ROOM_ALREADY_EXISTS (24),
ROOM_ALREADY_JOINED (36),
CHAT_SERVER_UNAVAILABLE (1000000),
ROOM_DIFFERENT_FACTION (1000001),
ROOM_NOT_GCW_DEFENDER_FACTION (1000005);
private static final Map<Integer, ChatResult> RESULT_MAP = new HashMap<>();
static {
for (ChatResult result : values()) {
RESULT_MAP.put(result.getCode(), result);
}
}
private final int code;
ChatResult(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static ChatResult fromInteger(int code) {
for (ChatResult result : ChatResult.values()) {
if (code == result.getCode())
return result;
}
return NONE;
ChatResult result = RESULT_MAP.get(code);
if (result == null)
return NONE;
return result;
}
}
+5 -24
View File
@@ -32,9 +32,7 @@ import network.packets.swg.zone.chat.ChatRoomMessage;
import resources.encodables.Encodable;
import resources.encodables.OutOfBandPackage;
import resources.network.NetBufferStream;
import resources.objects.player.PlayerObject;
import resources.persistable.Persistable;
import resources.player.Player;
import services.player.PlayerManager;
import java.io.IOException;
@@ -43,9 +41,6 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* @author Waverunner
*/
public class ChatRoom implements Encodable, Persistable {
private int id;
@@ -61,7 +56,7 @@ public class ChatRoom implements Encodable, Persistable {
// Members are only actually apart of a room when they're "in the room", so we don't need to save this info
// as each player will automatically re-join the room based on their joined channels list
private transient List<ChatAvatar> members;
// Large amount of data that's sent frequently, best if we can pre-encode everything and save it for later
private transient boolean modified = true;
private transient byte[] data;
@@ -233,31 +228,17 @@ public class ChatRoom implements Encodable, Persistable {
public void sendMessage(ChatAvatar sender, String message, OutOfBandPackage oob, PlayerManager playerManager) {
ChatRoomMessage chatRoomMessage = new ChatRoomMessage(sender, getId(), message, oob);
String senderName = sender.getName();
for (ChatAvatar member : members) {
Player player = playerManager.getPlayerFromNetworkId(member.getNetworkId());
if (player == null)
if (member.getPlayer().getPlayerObject().isIgnored(sender.getName()))
continue;
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
continue;
if (ghost.isIgnored(senderName))
continue;
player.sendPacket(chatRoomMessage);
member.getPlayer().sendPacket(chatRoomMessage);
}
}
public void sendPacketToMembers(PlayerManager manager, SWGPacket... packets) {
for (ChatAvatar member : members) {
Player player = manager.getPlayerFromNetworkId(member.getNetworkId());
if (player == null)
continue;
player.sendPacket(packets);
member.getPlayer().sendPacket(packets);
}
}
@@ -0,0 +1,55 @@
/************************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package resources.encodables;
public class CachedEncode {
private final EncodeOnly encodable;
private byte [] cached;
public CachedEncode(EncodeOnly encodable) {
this.encodable = encodable;
this.cached = null;
}
public void clearCached() {
this.cached = null;
}
public byte[] encode() {
if (cached == null)
cached = encodable.encode();
return cached;
}
public interface EncodeOnly {
byte [] encode();
}
}
-3
View File
@@ -29,9 +29,6 @@ package resources.encodables;
import java.nio.ByteBuffer;
/**
* @author Waverunner
*/
public interface Encodable {
byte [] encode();
void decode(ByteBuffer data);
+20 -13
View File
@@ -42,7 +42,6 @@ import resources.player.PlayerFlags;
import utilities.Encoder.StringType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PlayerObject extends IntangibleObject {
@@ -279,37 +278,45 @@ public class PlayerObject extends IntangibleObject {
sendDelta(8, 6, activeQuest);
}
public void removeFriend(String friend) {
play9.removeFriend(friend, this);
public boolean addFriend(String friend) {
return play9.addFriend(friend, this);
}
public void addFriend(String friend) {
play9.addFriend(friend, this);
public boolean removeFriend(String friend) {
return play9.removeFriend(friend, this);
}
public boolean isFriend(String target) {
return play9.isFriend(target);
}
public List<String> getFriendsList() {
return play9.getFriendsList();
}
public void addIgnored(String ignored) {
play9.addIgnored(ignored, this);
public void sendFriendsList() {
play9.sendFriendsList(this);
}
public void removeIgnored(String ignored) {
play9.removeIgnored(ignored, this);
public boolean addIgnored(String ignored) {
return play9.addIgnored(ignored, this);
}
public boolean removeIgnored(String ignored) {
return play9.removeIgnored(ignored, this);
}
public boolean isIgnored(String target) {
return play9.isIgnored(target);
}
public List<String> getIgnoreList() {
return play9.getIgnoreList();
}
public void sendIgnoreList() {
play9.sendIgnoreList(this);
}
public String getProfWheelPosition() {
return play8.getProfWheelPosition();
@@ -333,7 +340,7 @@ public class PlayerObject extends IntangibleObject {
}
public List<String> getJoinedChannels() {
return Collections.unmodifiableList(joinedChannels);
return new ArrayList<>(joinedChannels);
}
public boolean addJoinedChannel(String path) {
@@ -27,7 +27,9 @@
***********************************************************************************/
package resources.objects.player;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import resources.collections.SWGList;
import resources.collections.SWGSet;
@@ -58,50 +60,70 @@ class PlayerObjectPrivateNP implements Persistable {
}
public void removeFriend(String friend, SWGObject target) {
synchronized (friendsList) {
friendsList.remove(friend);
}
friendsList.sendDeltaMessage(target);
}
public void addFriend(String friend, SWGObject target) {
public boolean addFriend(String friend, SWGObject target) {
friend = friend.toLowerCase(Locale.US);
synchronized (friendsList) {
if (friendsList.contains(friend))
return false;
friendsList.add(friend);
}
friendsList.sendDeltaMessage(target);
return true;
}
public boolean removeFriend(String friend, SWGObject target) {
boolean changed;
synchronized (friendsList) {
changed = friendsList.remove(friend.toLowerCase(Locale.US));
}
friendsList.sendDeltaMessage(target);
return changed;
}
public boolean isFriend(String friend) {
synchronized (friendsList) {
return friendsList.contains(friend);
return friendsList.contains(friend.toLowerCase(Locale.US));
}
}
public List<String> getFriendsList() {
return friendsList;
return new ArrayList<>(friendsList);
}
public void addIgnored(String ignored, SWGObject target) {
public void sendFriendsList(SWGObject target) {
friendsList.sendRefreshedListData(target);
}
public boolean addIgnored(String ignored, SWGObject target) {
ignored = ignored.toLowerCase(Locale.US);
synchronized (ignoreList) {
if (ignoreList.contains(ignored))
return false;
ignoreList.add(ignored);
}
ignoreList.sendDeltaMessage(target);
return true;
}
public void removeIgnored(String ignored, SWGObject target) {
public boolean removeIgnored(String ignored, SWGObject target) {
boolean changed;
synchronized (ignoreList) {
ignoreList.remove(ignored);
changed = ignoreList.remove(ignored.toLowerCase(Locale.US));
}
ignoreList.sendDeltaMessage(target);
return changed;
}
public boolean isIgnored(String target) {
return ignoreList.contains(target);
return ignoreList.contains(target.toLowerCase(Locale.US));
}
public List<String> getIgnoreList() {
return ignoreList;
return new ArrayList<>(ignoreList);
}
public void sendIgnoreList(SWGObject target) {
ignoreList.sendRefreshedListData(target);
}
public void addDraftSchematic(String schematic, SWGObject target) {
+8
View File
@@ -125,6 +125,14 @@ public class Player implements Comparable<Player> {
return "";
}
public String getCharacterFirstName() {
String name = getCharacterName();
int spaceIndex = name.indexOf(' ');
if (spaceIndex != -1)
return name.substring(0, spaceIndex);
return name;
}
public int getUserId() {
return userId;
}
+120 -188
View File
@@ -49,7 +49,7 @@ import network.packets.swg.zone.object_controller.SpatialChat;
import resources.Terrain;
import resources.chat.ChatAvatar;
import resources.chat.ChatResult;
import resources.collections.SWGList;
import resources.control.Assert;
import resources.control.Manager;
import resources.encodables.OutOfBandPackage;
import resources.encodables.ProsePackage;
@@ -60,22 +60,20 @@ import resources.player.PlayerState;
import resources.server_info.Log;
import resources.server_info.RelationalServerData;
import resources.server_info.RelationalServerFactory;
import services.CoreManager;
import services.player.PlayerManager;
public class ChatManager extends Manager {
private final ChatRoomService roomService;
private final ChatMailService mailService;
private final RelationalServerData chatLogs;
private final PreparedStatement insertChatLog;
public ChatManager() {
roomService = new ChatRoomService();
mailService = new ChatMailService();
chatLogs = RelationalServerFactory.getServerDatabase("chat/chat_log.db");
insertChatLog = chatLogs.prepareStatement("INSERT INTO chat_log VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
addChildService(roomService);
addChildService(mailService);
@@ -90,37 +88,31 @@ public class ChatManager extends Manager {
Packet p = gpi.getPacket();
if (!(p instanceof SWGPacket))
return;
SWGPacket swg = (SWGPacket) p;
switch (swg.getPacketType()) {
switch (((SWGPacket) p).getPacketType()) {
case CHAT_INSTANT_MESSAGE_TO_CHARACTER:
if (p instanceof ChatInstantMessageToCharacter)
handleInstantMessage(gpi.getPlayerManager(), gpi.getPlayer(), (ChatInstantMessageToCharacter) p);
handleInstantMessage(gpi.getPlayer(), (ChatInstantMessageToCharacter) p);
break;
default:
break;
}
}
private void handlePlayerEventIntent(PlayerEventIntent pei) {
Player player = pei.getPlayer();
if (player == null)
return;
switch (pei.getEvent()) {
case PE_FIRST_ZONE:
updateChatAvatarStatus(player, true);
updateChatAvatarStatus(pei.getPlayer(), true);
break;
case PE_LOGGED_OUT:
if (player.getCreatureObject() == null)
break;
updateChatAvatarStatus(player, false);
updateChatAvatarStatus(pei.getPlayer(), false);
break;
default:
break;
default: break;
}
}
private void handleChatBroadcastIntent(ChatBroadcastIntent cbi) {
switch(cbi.getBroadcastType()) {
switch (cbi.getBroadcastType()) {
case AREA:
broadcastAreaMessage(cbi.getMessage(), cbi.getBroadcaster());
logChat(cbi.getBroadcaster(), ChatType.SYSTEM, ChatRange.LOCAL, cbi.getMessage());
@@ -134,19 +126,22 @@ public class ChatManager extends Manager {
logChat(cbi.getBroadcaster(), ChatType.SYSTEM, ChatRange.GALAXY, cbi.getMessage());
break;
case PERSONAL:
broadcastPersonalMessage(cbi.getProse(), cbi.getBroadcaster(), cbi.getMessage());
logChat(cbi.getBroadcaster(), ChatType.SYSTEM, ChatRange.PERSONAL, cbi.getMessage());
if (cbi.getProse() != null) {
broadcastPersonalMessage(cbi.getBroadcaster(), cbi.getProse());
logChat(cbi.getBroadcaster(), ChatType.SYSTEM, ChatRange.PERSONAL, "**OOB DATA**");
} else {
broadcastPersonalMessage(cbi.getBroadcaster(), cbi.getMessage());
logChat(cbi.getBroadcaster(), ChatType.SYSTEM, ChatRange.PERSONAL, cbi.getMessage());
}
break;
}
}
private void handleChatAvatarRequestIntent(ChatAvatarRequestIntent cari) {
switch (cari.getRequestType()) {
case TARGET_STATUS: {
Player player = cari.getPlayer();
sendTargetAvatarStatus(player, new ChatAvatar(0, cari.getTarget(), CoreManager.getGalaxy().getName()));
case TARGET_STATUS:
sendTargetAvatarStatus(cari.getPlayer(), new ChatAvatar(cari.getPlayer(), cari.getTarget()));
break;
}
case FRIEND_ADD_TARGET:
handleAddFriend(cari.getPlayer(), cari.getTarget());
break;
@@ -156,237 +151,183 @@ public class ChatManager extends Manager {
case FRIEND_LIST:
handleRequestFriendList(cari.getPlayer());
break;
case IGNORE_REMOVE_TARGET:
handleRemoveIgnored(cari.getPlayer(), cari.getTarget());
break;
case IGNORE_ADD_TARGET:
handleAddIgnored(cari.getPlayer(), cari.getTarget());
break;
case IGNORE_REMOVE_TARGET:
handleRemoveIgnored(cari.getPlayer(), cari.getTarget());
break;
case IGNORE_LIST:
handleRequestIgnoreList(cari.getPlayer());
break;
}
}
/* Friends List */
private void handleRequestFriendList(Player player) {
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
SWGList<String> friends = (SWGList<String>) ghost.getFriendsList();
friends.sendRefreshedListData(ghost);
}
private void handleRemoveFriend(Player player, String target) {
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
if (!ghost.isFriend(target)) {
sendSystemMessage(player, "@cmnty:friend_not_found", target);
return;
}
ghost.removeFriend(target);
sendSystemMessage(player, "@cmnty:friend_removed", target);
}
private void handleAddFriend(Player player, String target) {
if (target.equalsIgnoreCase(player.getCharacterName().split(" ")[0]))
if (target.equalsIgnoreCase(player.getCharacterFirstName()))
return;
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
if (ghost.isIgnored(target)) {
sendSystemMessage(player, "@cmnty:friend_fail_is_ignored", target);
return;
}
if (ghost.isFriend(target)) {
sendSystemMessage(player, "@cmnty:friend_duplicate", target);
return;
}
if (!player.getPlayerManager().playerExists(target)) {
sendSystemMessage(player, "@cmnty:friend_not_found", target);
return;
}
ghost.addFriend(target);
sendSystemMessage(player, "@cmnty:friend_added", target);
player.sendPacket(new ChatFriendsListUpdate(new ChatAvatar(0, target, CoreManager.getGalaxy().getName()), true));
}
/* Ignore List */
private void handleAddIgnored(Player player, String target) {
if (target.equalsIgnoreCase(player.getCharacterName().split(" ")[0]))
return;
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
if (ghost.isIgnored(target)) {
sendSystemMessage(player, "@cmnty:ignore_duplicate", target);
if (!ghost.addFriend(target)) {
sendSystemMessage(player, "@cmnty:friend_duplicate", target);
return;
}
sendSystemMessage(player, "@cmnty:friend_added", target);
sendTargetAvatarStatus(player, new ChatAvatar(null, target));
}
private void handleRemoveFriend(Player player, String target) {
if (!player.getPlayerObject().removeFriend(target)) {
sendSystemMessage(player, "@cmnty:friend_not_found", target);
return;
}
sendSystemMessage(player, "@cmnty:friend_removed", target);
}
private void handleRequestFriendList(Player player) {
player.getPlayerObject().sendFriendsList();
}
/* Ignore List */
private void handleAddIgnored(Player player, String target) {
if (target.equalsIgnoreCase(player.getCharacterFirstName()))
return;
if (!player.getPlayerManager().playerExists(target)) {
sendSystemMessage(player, "@cmnty:ignore_not_found", target);
return;
}
ghost.addIgnored(target);
player.getPlayerObject().removeFriend(target);
if (!player.getPlayerObject().addIgnored(target)) {
sendSystemMessage(player, "@cmnty:ignore_duplicate", target);
return;
}
sendSystemMessage(player, "@cmnty:ignore_added", target);
}
private void handleRemoveIgnored(Player player, String target) {
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
if (!ghost.isIgnored(target)) {
if (!player.getPlayerObject().removeIgnored(target)) {
sendSystemMessage(player, "@cmnty:ignore_not_found", target);
return;
}
ghost.removeIgnored(target);
sendSystemMessage(player, "@cmnty:ignore_removed", target);
}
private void handleRequestIgnoreList(Player player) {
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
SWGList<String> ignored = (SWGList<String>) ghost.getIgnoreList();
ignored.sendRefreshedListData(ghost);
player.getPlayerObject().sendIgnoreList();
}
/* Misc */
private void sendSystemMessage(Player player, String stringId, String target) {
new ChatBroadcastIntent(player, new ProsePackage("StringId", stringId, "TT", target)).broadcast();
}
private void handleSpatialChatIntent(SpatialChatIntent spi) {
Player sender = spi.getPlayer();
SWGObject actor = sender.getCreatureObject();
String senderName = sender.getCharacterFirstName();
// Send to self
SpatialChat message = new SpatialChat(actor.getObjectId(), actor.getObjectId(), 0, spi.getMessage(), (short) spi.getChatType(), (short) spi.getMoodId());
sender.sendPacket(message);
logChat(sender, ChatType.SPATIAL, ChatRange.LOCAL, spi.getMessage());
String senderName = ChatAvatar.getFromPlayer(sender).getName();
// Notify observers of the chat message
for (Player owner : actor.getObservers()) {
PlayerObject awareGhost = owner.getPlayerObject();
if (!awareGhost.isIgnored(senderName))
owner.sendPacket(new SpatialChat(owner.getCreatureObject().getObjectId(), message));
if (owner.getPlayerObject().isIgnored(senderName))
continue;
owner.sendPacket(new SpatialChat(owner.getCreatureObject().getObjectId(), message));
}
}
private void handleInstantMessage(PlayerManager playerMgr, Player sender, ChatInstantMessageToCharacter request) {
private void handleInstantMessage(Player sender, ChatInstantMessageToCharacter request) {
String strReceiver = request.getCharacter().toLowerCase(Locale.ENGLISH);
String strSender = sender.getCharacterName();
String strSender = sender.getCharacterFirstName();
if (strSender.contains(" "))
strSender = strSender.split(" ")[0];
Player receiver = sender.getPlayerManager().getPlayerByCreatureFirstName(strReceiver);
Player receiver = playerMgr.getPlayerByCreatureFirstName(strReceiver);
int errorCode = 0; // 0 = No issue, 4 = "strReceiver is not online"
ChatResult result = ChatResult.SUCCESS;
if (receiver == null || receiver.getPlayerState() != PlayerState.ZONED_IN)
errorCode = 4;
if (errorCode != 4 && receiver.getPlayerObject() != null && receiver.getPlayerObject().isIgnored(strSender))
errorCode = ChatResult.IGNORED.getCode();
sender.sendPacket(new ChatOnSendInstantMessage(errorCode, request.getSequence()));
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
else if (receiver.getPlayerObject().isIgnored(strSender))
result = ChatResult.IGNORED;
if (errorCode != 0)
sender.sendPacket(new ChatOnSendInstantMessage(result.getCode(), request.getSequence()));
if (result != ChatResult.SUCCESS)
return;
receiver.sendPacket(new ChatInstantMessageToClient(request.getGalaxy(), strSender, request.getMessage()));
logChat(sender, receiver, ChatType.TELL, request.getMessage());
}
/* Friends */
private void updateChatAvatarStatus(Player player, boolean online) {
PlayerManager playerManager = player.getPlayerManager();
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
String galaxy = CoreManager.getGalaxy().getName();
if (online) {
PlayerObject playerObject = player.getPlayerObject();
if (playerObject != null && playerObject.getFriendsList().size() <= 0) {
for (String friend : playerObject.getFriendsList()) {
sendTargetAvatarStatus(player, new ChatAvatar(0, friend, galaxy));
}
for (String friend : player.getPlayerObject().getFriendsList()) {
sendTargetAvatarStatus(player, new ChatAvatar(null, friend));
}
}
final ChatFriendsListUpdate update = new ChatFriendsListUpdate(avatar, online);
playerManager.notifyPlayers(playerNotified -> {
String playerName = player.getCharacterFirstName().toLowerCase(Locale.US);
player.getPlayerManager().notifyPlayers(playerNotified -> {
if (playerNotified.getPlayerState() != PlayerState.ZONED_IN)
return false;
PlayerObject playerObject = playerNotified.getPlayerObject();
return playerObject != null && playerObject.getFriendsList().contains(update.getFriend().getName());
}, update);
return playerNotified.getPlayerObject().isFriend(playerName);
}, new ChatFriendsListUpdate(new ChatAvatar(player, playerName), online));
}
private void sendTargetAvatarStatus(Player player, ChatAvatar target) {
PlayerObject object = player.getPlayerObject();
if (object == null)
return;
Player targetPlayer = player.getPlayerManager().getPlayerByCreatureFirstName(target.getName());
boolean online = true;
if (targetPlayer == null || targetPlayer.getPlayerState() != PlayerState.ZONED_IN)
online = false;
player.sendPacket(new ChatFriendsListUpdate(target, online));
}
private void broadcastAreaMessage(String message, Player broadcaster) {
ChatSystemMessage packet = new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT.ordinal(), message);
broadcaster.sendPacket(packet);
broadcaster.getCreatureObject().sendObservers(packet);
player.sendPacket(new ChatFriendsListUpdate(target, targetPlayer != null && targetPlayer.getPlayerState() == PlayerState.ZONED_IN));
}
private void broadcastAreaMessage(String message, Player broadcaster) {
broadcaster.getCreatureObject().sendObserversAndSelf(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT.ordinal(), message));
}
private void broadcastPlanetMessage(String message, Terrain terrain) {
ChatSystemMessage packet = new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT.ordinal(), message);
new NotifyPlayersPacketIntent(packet, terrain, null).broadcast();
new NotifyPlayersPacketIntent(packet, terrain).broadcast();
}
private void broadcastGalaxyMessage(String message) {
ChatSystemMessage packet = new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT.ordinal(), message);
new NotifyPlayersPacketIntent(packet).broadcast();
}
private void broadcastPersonalMessage(ProsePackage prose, Player player, String message) {
if (prose != null)
player.sendPacket(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT, new OutOfBandPackage(prose)));
else
player.sendPacket(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT, message));
private void broadcastPersonalMessage(Player player, String message) {
player.sendPacket(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT, message));
}
private void broadcastPersonalMessage(Player player, ProsePackage prose) {
player.sendPacket(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT, new OutOfBandPackage(prose)));
}
private void logChat(Player broadcaster, ChatType type, ChatRange range, String message) {
@@ -414,8 +355,7 @@ public class ChatManager extends Manager {
}
private void logChat(long sendId, String sendName, long recvId, String recvName, String type, String range, String room, String subject, String message) {
if (message == null)
return;
Assert.notNull(message, "Message cannot be null!");
try {
synchronized (insertChatLog) {
insertChatLog.setLong(1, System.currentTimeMillis());
@@ -435,20 +375,12 @@ public class ChatManager extends Manager {
}
}
public static enum ChatType {
MAIL,
TELL,
SYSTEM,
SPATIAL,
CHAT
public enum ChatType {
MAIL, TELL, SYSTEM, SPATIAL, CHAT
}
public static enum ChatRange {
PERSONAL,
ROOM,
LOCAL,
TERRAIN,
GALAXY
public enum ChatRange {
PERSONAL, ROOM, LOCAL, TERRAIN, GALAXY
}
}
+125
View File
@@ -0,0 +1,125 @@
/************************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package services.chat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import resources.chat.ChatRoom;
import resources.control.Assert;
import resources.server_info.Log;
public class ChatRoomContainer {
private final Object mapMutex;
private final Map<Integer, ChatRoom> idMap;
private final Map<String, ChatRoom> pathMap;
public ChatRoomContainer() {
this.mapMutex = new Object();
this.idMap = new HashMap<>();
this.pathMap = new HashMap<>();
}
public boolean addRoom(ChatRoom room) {
Assert.notNull(room, "ChatRoom cannot be null!");
Assert.test(room.getId() > 0, "ChatRoom ID must be > 0!");
Assert.notNull(room.getPath(), "Path must not be null!");
Assert.test(!room.getPath().isEmpty(), "Path must be non-empty!");
Assert.test(room.getPath().startsWith("SWG."), "Path must start with \"SWG.\"!");
synchronized (mapMutex) {
ChatRoom overwrittenId = idMap.put(room.getId(), room);
ChatRoom overwrittenPath = pathMap.put(room.getPath(), room);
if (overwrittenId != null || overwrittenPath != null) {
if (overwrittenId != overwrittenPath)
Log.w("Internal maps in ChatRoomContainer are out of sync! ID and Path don't match for room: %s", room);
if (overwrittenId != null)
idMap.put(overwrittenId.getId(), overwrittenId);
else
idMap.put(overwrittenPath.getId(), overwrittenPath);
if (overwrittenPath != null)
pathMap.put(overwrittenPath.getPath(), overwrittenPath);
else
pathMap.put(overwrittenId.getPath(), overwrittenId);
return false;
}
}
return true;
}
public boolean destroyRoom(ChatRoom room) {
Assert.notNull(room, "ChatRoom cannot be null!");
synchronized (mapMutex) {
boolean success = true;
success = idMap.remove(room.getId()) != null && success;
success = pathMap.remove(room.getPath()) != null && success;
return success;
}
}
public List<ChatRoom> getAllRooms() {
synchronized (mapMutex) {
return new ArrayList<>(idMap.values());
}
}
public ChatRoom getRoomById(int roomId) {
Assert.test(roomId > 0, "Room ID must be > 0!");
synchronized (mapMutex) {
return idMap.get(roomId);
}
}
public ChatRoom getRoomByPath(String path) {
Assert.notNull(path, "Path must not be null!");
Assert.test(!path.isEmpty(), "Path must be non-empty!");
Assert.test(path.startsWith("SWG."), "Path must start with \"SWG.\"!");
synchronized (mapMutex) {
return pathMap.get(path);
}
}
public boolean hasRoomWithId(int roomId) {
Assert.test(roomId > 0, "Room ID must be > 0!");
synchronized (mapMutex) {
return idMap.containsKey(roomId);
}
}
public boolean hasRoomWithPath(String path) {
Assert.notNull(path, "Path must not be null!");
Assert.test(!path.isEmpty(), "Path must be non-empty!");
Assert.test(path.startsWith("SWG."), "Path must start with \"SWG.\"!");
synchronized (mapMutex) {
return pathMap.containsKey(path);
}
}
}
+366
View File
@@ -0,0 +1,366 @@
/************************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package services.chat;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import network.packets.swg.zone.chat.ChatOnDestroyRoom;
import network.packets.swg.zone.chat.ChatOnEnteredRoom;
import network.packets.swg.zone.chat.ChatOnLeaveRoom;
import network.packets.swg.zone.chat.ChatOnSendRoomMessage;
import network.packets.swg.zone.insertion.ChatRoomList;
import resources.chat.ChatAvatar;
import resources.chat.ChatResult;
import resources.chat.ChatRoom;
import resources.client_info.ServerFactory;
import resources.client_info.visitors.DatatableData;
import resources.control.Assert;
import resources.encodables.OutOfBandPackage;
import resources.player.AccessLevel;
import resources.player.Player;
import resources.server_info.CachedObjectDatabase;
import resources.server_info.Log;
import resources.server_info.ObjectDatabase;
import resources.server_info.RelationalServerData;
import resources.server_info.RelationalServerFactory;
import services.CoreManager;
import services.chat.ChatManager.ChatRange;
import services.chat.ChatManager.ChatType;
public class ChatRoomHandler {
private final ObjectDatabase<ChatRoom> database;
private final ChatRoomContainer rooms;
private final AtomicInteger maxChatRoomId;
private final Object roomCreationMutex;
private RelationalServerData chatLogs;
private PreparedStatement insertChatLog;
public ChatRoomHandler() {
this.database = new CachedObjectDatabase<ChatRoom>("odb/chat_rooms.db", ChatRoom::create, (r, s) -> r.save(s));
this.rooms = new ChatRoomContainer();
this.maxChatRoomId = new AtomicInteger(0);
this.roomCreationMutex = new Object();
}
public boolean initialize() {
database.load();
database.traverse((room) -> {
if (room.getId() >= maxChatRoomId.get())
maxChatRoomId.set(room.getId());
if (room.getOwner().equals(ChatAvatar.getSystemAvatar()))
return;
rooms.addRoom(room);
});
chatLogs = RelationalServerFactory.getServerDatabase("chat/chat_log.db");
insertChatLog = chatLogs.prepareStatement("INSERT INTO chat_log VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
createSystemChannels();
return true;
}
public boolean terminate() {
database.save();
database.close();
chatLogs.close();
return true;
}
public void enterChatChannels(Player player) {
for (String channel : player.getPlayerObject().getJoinedChannels()) {
enterChatChannel(player, channel, false);
}
}
public void enterPlanetaryChatChannels(Player player) {
// Leave old zone-only chat channels
for (String channel : new ArrayList<>(player.getPlayerObject().getJoinedChannels())) {
if (channel.endsWith(".Planet") || channel.endsWith(".system")) {
leaveChatChannel(player, channel);
}
}
// Enter the new zone-only chat channels
String planetPath = "SWG." + player.getGalaxyName() + "." + player.getCreatureObject().getTerrain().getName() + ".";
Assert.test(rooms.hasRoomWithPath(planetPath + "Planet"), "Planet chat does not exist! planetPath = " + planetPath);
Assert.test(rooms.hasRoomWithPath(planetPath + "system"), "System chat does not exist! planetPath = " + planetPath);
enterChatChannel(player, planetPath + "Planet", false);
enterChatChannel(player, planetPath + "system", false);
}
/**
* Attempts to join the specified chat channel
*
* @param player Player joining the chat channel
* @param room Chat room to enter
*/
public void enterChatChannel(Player player, ChatRoom room, int sequence, boolean ignoreInvitation) {
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
ChatResult result = room.canJoinRoom(avatar, ignoreInvitation);
if (player.getAccessLevel() != AccessLevel.PLAYER)
result = ChatResult.SUCCESS;
Log.d("enterChatChannel user=%s player=%s room=%s result=%s", player.getUsername(), player.getCharacterName(), room.getPath(), result);
if (result != ChatResult.SUCCESS) {
player.sendPacket(new ChatOnEnteredRoom(avatar, result, room.getId(), sequence));
return;
}
// TODO: Check if player is appropriate faction for the room (Rebel and imperial chat rooms)
// Server-based list so we can join chat channels automatically
player.getPlayerObject().addJoinedChannel(room.getPath());
// Re-send the player the room list with just this room as it could have been public/hidden
// This also "refreshes" the client, not sending this will cause a Chat channel unavailable message.
if (!room.isPublic())
player.sendPacket(new ChatRoomList(room));
// Notify players of success, it's ChatResult.SUCCESS at this point
player.sendPacket(new ChatOnEnteredRoom(avatar, result, room.getId(), sequence));
// Notify everyone that a player entered the room
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnEnteredRoom(avatar, result, room.getId(), 0));
room.addMember(avatar);
}
public void enterChatChannel(Player player, int id, int sequence) {
ChatRoom room = rooms.getRoomById(id);
if (room == null) {
player.sendPacket(new ChatOnEnteredRoom(ChatAvatar.getFromPlayer(player), ChatResult.NONE, id, sequence));
return;
}
enterChatChannel(player, room, sequence, false);
}
public void enterChatChannel(Player player, String path, boolean ignoreInvitation) {
ChatRoom room = rooms.getRoomByPath(path);
if (room == null) {
// Channel was not found, attempt to remove it from this players list of channels if it exists.
// This can happen if a channel was deleted while the player was offline
player.getPlayerObject().removeJoinedChannel(path);
return;
}
enterChatChannel(player, room, 0, ignoreInvitation);
}
public void leaveChatChannel(Player player, ChatRoom room, int sequence) {
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
if (!room.removeMember(avatar) && !player.getPlayerObject().removeJoinedChannel(room.getPath()))
return;
Log.d("leaveChatChannel user=%s player=%s room=%s", player.getUsername(), player.getCharacterName(), room.getPath());
player.sendPacket(new ChatOnLeaveRoom(avatar, ChatResult.SUCCESS.getCode(), room.getId(), sequence));
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnLeaveRoom(avatar, ChatResult.SUCCESS.getCode(), room.getId(), 0));
}
public void leaveChatChannel(Player player, String path) {
ChatRoom room = rooms.getRoomByPath(path);
if (room == null)
return;
leaveChatChannel(player, room, 0);
}
/**
* Creates a new chat room with the specified address path. If the path's parent channel doesn't exist, then a new chat room is created with the same passed arguments.
*
* @param creator Room creator who will also become the owner of this room
* @param isPublic Determines if the room should be publicly displayed in the channel listing
* @param moderated Determines if the room should be moderated
* @param path Address for the channel (Ex: SWG.serverName.Imperial)
* @param title Descriptive name of the chat channel (Ex: Imperial chat for this galaxy)
* @param persist If true then this channel will be saved in an {@link ObjectDatabase}
* @return {@link ChatRoom}
*/
public void createRoom(ChatAvatar creator, boolean isPublic, boolean moderated, String path, String title, boolean persist) {
Assert.notNull(path, "Path cannot be empty!");
Assert.test(!path.isEmpty(), "Path cannot be empty!");
Assert.test(path.startsWith("SWG."+creator.getGalaxy()) && !path.equals("SWG."+creator.getGalaxy()), "Invalid path! " + path);
synchronized (roomCreationMutex) {
if (rooms.getRoomByPath(path) != null)
return;
// All paths should have parents, lets validate to make sure they exist first. Create them if they don't.
// This chunk of code makes this function recursive
int lastIndex = path.lastIndexOf('.', path.length());
if (lastIndex != -1) {
String parentPath = path.substring(0, lastIndex);
if (!parentPath.equals("SWG."+creator.getGalaxy()))
createRoom(creator, isPublic, false, parentPath, "", persist);
}
ChatRoom room = new ChatRoom();
room.setId(maxChatRoomId.incrementAndGet());
room.setOwner(creator);
room.setCreator(creator);
room.setIsPublic(isPublic);
room.setModerated(moderated);
room.setPath(path);
room.setTitle(title);
room.addModerator(creator);
rooms.addRoom(room);
if (persist)
database.add(room);
}
}
public void sendMessageToRoom(Player player, int roomId, int sequence, String message, OutOfBandPackage oobPackage) {
sendMessageToRoom(player, rooms.getRoomById(roomId), sequence, message, oobPackage);;
}
public void sendMessageToRoom(Player player, String path, int sequence, String message, OutOfBandPackage oobPackage) {
sendMessageToRoom(player, rooms.getRoomByPath(path), sequence, message, oobPackage);;
}
public void sendMessageToRoom(Player player, ChatRoom room, int sequence, String message, OutOfBandPackage oobPackage) {
if (room == null) {
player.sendPacket(new ChatOnSendRoomMessage(ChatResult.ROOM_INVALID_ID.getCode(), sequence));
return;
}
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
ChatResult result = room.canSendMessage(avatar);
if (result == ChatResult.SUCCESS && message.length() > 512)
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
player.sendPacket(new ChatOnSendRoomMessage(result.getCode(), sequence));
if (result == ChatResult.SUCCESS) {
room.sendMessage(avatar, message, oobPackage, player.getPlayerManager());
logChat(player.getCreatureObject().getObjectId(), player.getCharacterName(), room.getId() + "/" + room.getPath(), message);
}
}
public boolean notifyDestroyRoom(ChatAvatar destroyer, String roomPath, int sequence) {
ChatRoom room;
synchronized (roomCreationMutex) {
room = rooms.getRoomByPath(roomPath);
if (room == null)
return false;
rooms.destroyRoom(room);
}
// Send the ChatOnDestroyRoom packet to every else in the room besides the person destroying the packet
ChatOnDestroyRoom packet = new ChatOnDestroyRoom(destroyer, ChatResult.SUCCESS.getCode(), room.getId(), 0);
room.getMembers().forEach(member -> {
if (!destroyer.equals(member))
member.getPlayer().sendPacket(packet);
});
return true;
}
private void createSystemChannels() {
/**
* Channel Notes
* Group channels: created by System
* SWG.serverName.group.GroupObjectId.GroupChat title = GroupId
* Guild channels: created by System
* SWG.serverName.guild.GuildId.GuildChat title = GuildId
* City channels: created by System
* SWG.serverName.city.CityId.CityChat title = CityId
*/
String galaxy = CoreManager.getGalaxy().getName();
ChatAvatar systemAvatar = ChatAvatar.getSystemAvatar();
String basePath = "SWG." + galaxy + ".";
DatatableData rooms = ServerFactory.getDatatable("chat/default_rooms.iff");
rooms.handleRows((r) -> {
Log.d("Creating room: %s\t\t'%s'", basePath + rooms.getCell(r, 0), rooms.getCell(r, 1));
createRoom(systemAvatar, true, false, basePath + rooms.getCell(r, 0), (String) rooms.getCell(r, 1), false);
});
createPlanetChannels(systemAvatar, basePath);
/*
* Battlefield Room path examples: SWG.Bria.corellia.battlefield SWG.Bria.corellia.battlefield.corellia_mountain_fortress.allFactions SWG.Bria.corellia.battlefield.corellia_pvp.allFactions / Imperial / Rebel SWG.Bria.corellia.battlefield.corellia_rebel_riverside_fort.allFactions
*/
}
private void createPlanetChannels(ChatAvatar systemAvatar, String basePath) {
DatatableData planets = ServerFactory.getDatatable("chat/planets.iff");
planets.handleRows((r) -> {
String path = basePath + planets.getCell(r, 0) + ".";
createRoom(systemAvatar, true, false, path + "Planet", "public chat for this planet, cannot create rooms here", false);
createRoom(systemAvatar, true, false, path + "system", "system messages for this planet, cannot create rooms here", false);
createRoom(systemAvatar, true, false, path + "Chat", "public chat for this planet, can create rooms here", false);
});
}
public ChatRoom getRoomById(int roomId) {
return rooms.getRoomById(roomId);
}
public ChatRoom getRoomByPath(String path) {
return rooms.getRoomByPath(path);
}
public List<ChatRoom> getRoomList(Player player) {
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
List<ChatRoom> ret = new ArrayList<>();
for (ChatRoom chatRoom : rooms.getAllRooms()) {
if (!chatRoom.isPublic() && !chatRoom.isInvited(avatar) && !chatRoom.getOwner().equals(avatar))
continue;
ret.add(chatRoom);
}
return ret;
}
public void logChat(long sendId, String sendName, String room, String message) {
try {
synchronized (insertChatLog) {
insertChatLog.setLong(1, System.currentTimeMillis());
insertChatLog.setLong(2, sendId);
insertChatLog.setString(3, sendName);
insertChatLog.setLong(4, 0);
insertChatLog.setString(5, "");
insertChatLog.setString(6, ChatType.CHAT.name());
insertChatLog.setString(7, ChatRange.ROOM.name());
insertChatLog.setString(8, room);
insertChatLog.setString(9, "");
insertChatLog.setString(10, message);
insertChatLog.executeUpdate();
}
} catch (SQLException e) {
Log.e(e);
}
}
}
+121 -592
View File
@@ -27,7 +27,6 @@
package services.chat;
import intents.NotifyPlayersPacketIntent;
import intents.PlayerEventIntent;
import intents.chat.ChatRoomUpdateIntent;
import intents.network.GalacticPacketIntent;
@@ -44,13 +43,10 @@ import network.packets.swg.zone.chat.ChatOnAddModeratorToRoom;
import network.packets.swg.zone.chat.ChatOnBanAvatarFromRoom;
import network.packets.swg.zone.chat.ChatOnCreateRoom;
import network.packets.swg.zone.chat.ChatOnDestroyRoom;
import network.packets.swg.zone.chat.ChatOnEnteredRoom;
import network.packets.swg.zone.chat.ChatOnInviteToRoom;
import network.packets.swg.zone.chat.ChatOnKickAvatarFromRoom;
import network.packets.swg.zone.chat.ChatOnLeaveRoom;
import network.packets.swg.zone.chat.ChatOnReceiveRoomInvitation;
import network.packets.swg.zone.chat.ChatOnRemoveModeratorFromRoom;
import network.packets.swg.zone.chat.ChatOnSendRoomMessage;
import network.packets.swg.zone.chat.ChatOnUnbanAvatarFromRoom;
import network.packets.swg.zone.chat.ChatOnUninviteFromRoom;
import network.packets.swg.zone.chat.ChatQueryRoom;
@@ -62,53 +58,19 @@ import network.packets.swg.zone.chat.ChatSendToRoom;
import network.packets.swg.zone.chat.ChatUnbanAvatarFromRoom;
import network.packets.swg.zone.chat.ChatUninviteFromRoom;
import network.packets.swg.zone.insertion.ChatRoomList;
import resources.Terrain;
import resources.chat.ChatAvatar;
import resources.chat.ChatResult;
import resources.chat.ChatRoom;
import resources.client_info.ServerFactory;
import resources.client_info.visitors.DatatableData;
import resources.control.Assert;
import resources.control.Service;
import resources.objects.player.PlayerObject;
import resources.player.AccessLevel;
import resources.player.Player;
import resources.server_info.CachedObjectDatabase;
import resources.server_info.Log;
import resources.server_info.ObjectDatabase;
import resources.server_info.RelationalServerData;
import resources.server_info.RelationalServerFactory;
import resources.server_info.SynchronizedMap;
import services.CoreManager;
import services.chat.ChatManager.ChatRange;
import services.chat.ChatManager.ChatType;
import services.player.PlayerManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import resources.encodables.OutOfBandPackage;
/**
* @author Waverunner
*/
public class ChatRoomService extends Service {
// Map to keep track of each player's recent message for a room to prevent duplicates from client
private final ObjectDatabase<ChatRoom> database;
private final Map<Integer, ChatRoom> roomMap;
private final AtomicInteger maxChatRoomId;
private RelationalServerData chatLogs;
private PreparedStatement insertChatLog;
private final ChatRoomHandler chatRoomHandler;
public ChatRoomService() {
database = new CachedObjectDatabase<ChatRoom>("odb/chat_rooms.db", ChatRoom::create, (r, s) -> r.save(s));
roomMap = new SynchronizedMap<>();
maxChatRoomId = new AtomicInteger(0);
chatRoomHandler = new ChatRoomHandler();
registerForIntent(ChatRoomUpdateIntent.class, crui -> handleChatRoomUpdateIntent(crui));
registerForIntent(GalacticPacketIntent.class, gpi -> handleGalacticPacketIntent(gpi));
@@ -117,24 +79,12 @@ public class ChatRoomService extends Service {
@Override
public boolean initialize() {
database.load();
database.traverse((room) -> {
if (room.getId() >= maxChatRoomId.get())
maxChatRoomId.set(room.getId());
roomMap.put(room.getId(), room);
});
chatLogs = RelationalServerFactory.getServerDatabase("chat/chat_log.db");
insertChatLog = chatLogs.prepareStatement("INSERT INTO chat_log VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
createSystemChannels(CoreManager.getGalaxy().getName());
return super.initialize();
return super.initialize() && chatRoomHandler.initialize();
}
@Override
public boolean terminate() {
database.close();
chatLogs.close();
return super.terminate();
return chatRoomHandler.terminate() && super.terminate();
}
private void handleGalacticPacketIntent(GalacticPacketIntent gpi) {
@@ -149,16 +99,13 @@ public class ChatRoomService extends Service {
if (p instanceof ChatQueryRoom)
handleChatQueryRoom(player, (ChatQueryRoom) p);
break;
case CHAT_ENTER_ROOM_BY_ID: {
if (!(p instanceof ChatEnterRoomById))
return;
ChatEnterRoomById enterRoomById = (ChatEnterRoomById) p;
enterChatChannel(player, enterRoomById.getRoomId(), enterRoomById.getSequence());
case CHAT_ENTER_ROOM_BY_ID:
if (p instanceof ChatEnterRoomById)
chatRoomHandler.enterChatChannel(player, ((ChatEnterRoomById) p).getRoomId(), ((ChatEnterRoomById) p).getSequence());
break;
}
case CHAT_REMOVE_AVATAR_FROM_ROOM:
if (p instanceof ChatRemoveAvatarFromRoom)
leaveChatChannel(player, ((ChatRemoveAvatarFromRoom) p).getPath());
chatRoomHandler.leaveChatChannel(player, ((ChatRemoveAvatarFromRoom) p).getPath());
break;
case CHAT_SEND_TO_ROOM:
if (p instanceof ChatSendToRoom)
@@ -210,17 +157,10 @@ public class ChatRoomService extends Service {
}
private void handlePlayerEventIntent(PlayerEventIntent pei) {
Player player = pei.getPlayer();
if (player == null)
return;
switch (pei.getEvent()) {
case PE_ZONE_IN_SERVER:
enterPlanetaryChatChannels(player);
break;
case PE_FIRST_ZONE:
if (player.getPlayerObject() != null)
enterChatChannels(player, player.getPlayerObject().getJoinedChannels());
case PE_ZONE_IN_CLIENT:
chatRoomHandler.enterChatChannels(pei.getPlayer());
chatRoomHandler.enterPlanetaryChatChannels(pei.getPlayer());
break;
default:
break;
@@ -230,19 +170,19 @@ public class ChatRoomService extends Service {
private void handleChatRoomUpdateIntent(ChatRoomUpdateIntent crui) {
switch (crui.getUpdateType()) {
case CREATE:
createRoom(crui.getAvatar(), crui.isPublic(), crui.getPath(), crui.getTitle());
chatRoomHandler.createRoom(crui.getAvatar(), crui.isPublic(), false, crui.getPath(), crui.getTitle(), false);
break;
case DESTROY:
notifyDestroyRoom(crui.getAvatar(), crui.getPath(), 0);
chatRoomHandler.notifyDestroyRoom(crui.getAvatar(), crui.getPath(), 0);
break;
case JOIN:
enterChatChannel(crui.getPlayer(), crui.getPath(), crui.isIgnoreInvitation());
chatRoomHandler.enterChatChannel(crui.getPlayer(), crui.getPath(), crui.isIgnoreInvitation());
break;
case LEAVE:
leaveChatChannel(crui.getPlayer(), crui.getPath());
chatRoomHandler.leaveChatChannel(crui.getPlayer(), crui.getPath());
break;
case SEND_MESSAGE:
sendMessageToRoom(crui.getPlayer(), getRoom(crui.getPath()), 0, crui.getMessage(), new OutOfBandPackage());
chatRoomHandler.sendMessageToRoom(crui.getPlayer(), crui.getPath(), 0, crui.getMessage(), new OutOfBandPackage());
break;
default:
break;
@@ -252,307 +192,180 @@ public class ChatRoomService extends Service {
/* Chat Rooms */
private void handleChatRemoveModeratorFromRoom(Player player, ChatRemoveModeratorFromRoom p) {
String path = p.getRoom();
ChatAvatar target = p.getAvatar();
int sequence = p.getSequence();
ChatRoom room = getRoom(path);
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null) {
player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, ChatResult.ROOM_INVALID_NAME.getCode(), path, sequence));
return;
}
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (!room.isModerated())
result = ChatResult.CUSTOM_FAILURE;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (!room.removeModerator(target))
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
if (!room.isModerated()) {
player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, ChatResult.CUSTOM_FAILURE.getCode(), path, sequence));
return;
}
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), path, sequence));
return;
}
if (!room.removeModerator(target)) {
player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, ChatResult.TARGET_AVATAR_DOESNT_EXIST.getCode(), path, sequence));
return;
}
player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, ChatResult.SUCCESS.getCode(), path, sequence));
player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, result.getCode(), p.getRoom(), p.getSequence()));
}
private void handleChatAddModeratorToRoom(Player player, ChatAddModeratorToRoom p) {
String path = p.getRoom();
ChatAvatar target = p.getAvatar();
int sequence = p.getSequence();
ChatRoom room = getRoom(path);
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null) {
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.ROOM_INVALID_NAME.getCode(), path, sequence));
return;
}
if (!room.isModerated()) {
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.CUSTOM_FAILURE.getCode(), path, sequence));
return;
}
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), path, sequence));
return;
}
Player targetPlayer = player.getPlayerManager().getPlayerByCreatureFirstName(target.getName());
if (targetPlayer == null) {
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.TARGET_AVATAR_DOESNT_EXIST.getCode(), path, sequence));
return;
}
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (!room.isModerated())
result = ChatResult.CUSTOM_FAILURE;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (!room.removeModerator(target) || player.getPlayerManager().getPlayerByCreatureFirstName(target.getName()) == null)
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
else if (room.addModerator(target))
result = ChatResult.NONE;
if (room.removeBanned(target)) {
// Remove player from the ban list for players that have joined the room, since this player is now a moderator
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnUnbanAvatarFromRoom(path, sender, target, ChatResult.SUCCESS.getCode(), 0));
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnUnbanAvatarFromRoom(p.getRoom(), sender, target, ChatResult.SUCCESS.getCode(), 0));
}
if (!room.addModerator(target)) {
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.NONE.getCode(), path, sequence));
return;
}
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.SUCCESS.getCode(), path, sequence));
player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, result.getCode(), p.getRoom(), p.getSequence()));
}
private void handleChatUnbanAvatarFromRoom(Player player, ChatUnbanAvatarFromRoom p) {
String path = p.getRoom();
ChatAvatar target = p.getAvatar();
int sequence = p.getSequence();
ChatRoom room = getRoom(path);
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null) {
player.sendPacket(new ChatOnUnbanAvatarFromRoom(path, sender, target, ChatResult.ROOM_INVALID_NAME.getCode(), sequence));
return;
}
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (!room.isBanned(target) || !room.removeBanned(target))
result = ChatResult.ROOM_AVATAR_BANNED;
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnUnbanAvatarFromRoom(path, sender, target, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), sequence));
return;
}
if (!room.isBanned(target) || !room.removeBanned(target)) {
player.sendPacket(new ChatOnUnbanAvatarFromRoom(path, sender, target, ChatResult.ROOM_AVATAR_BANNED.getCode(), sequence));
return;
}
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnUnbanAvatarFromRoom(path, sender, target, ChatResult.SUCCESS.getCode(), sequence));
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnUnbanAvatarFromRoom(p.getRoom(), sender, target, result.getCode(), p.getSequence()));
}
private void handleChatBanAvatarFromRoom(Player player, ChatBanAvatarFromRoom p) {
String path = p.getRoom();
ChatAvatar target = p.getAvatar();
int sequence = p.getSequence();
ChatRoom room = getRoom(path);
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null) {
player.sendPacket(new ChatOnBanAvatarFromRoom(path, sender, target, ChatResult.ROOM_INVALID_NAME.getCode(), sequence));
return;
}
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnBanAvatarFromRoom(path, sender, target, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), sequence));
return;
}
if (room.isBanned(target)) {
player.sendPacket(new ChatOnBanAvatarFromRoom(path, sender, target, ChatResult.ROOM_AVATAR_BANNED.getCode(), sequence));
return;
}
if (!room.isMember(target)) {
player.sendPacket(new ChatOnBanAvatarFromRoom(path, sender, target, ChatResult.TARGET_AVATAR_DOESNT_EXIST.getCode(), sequence));
return;
}
if (room.isModerator(target))
room.removeModerator(target);
if (room.isInvited(target))
room.removeInvited(target);
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (room.isBanned(target))
result = ChatResult.ROOM_AVATAR_BANNED;
else if (!room.isMember(target))
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
room.removeModerator(target);
room.removeInvited(target);
room.addBanned(target);
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnBanAvatarFromRoom(path, sender, target, ChatResult.SUCCESS.getCode(), sequence));
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnBanAvatarFromRoom(p.getRoom(), sender, target, result.getCode(), p.getSequence()));
}
private void handleChatKickAvatarFromRoom(Player player, ChatKickAvatarFromRoom p) {
String path = p.getRoom();
ChatAvatar target = p.getAvatar();
ChatRoom room = getRoom(path);
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null) {
player.sendPacket(new ChatOnKickAvatarFromRoom(target, sender, ChatResult.ROOM_INVALID_NAME.getCode(), path));
return;
}
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (!room.isMember(target))
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
else if (player.getPlayerManager().getPlayerByCreatureFirstName(target.getName()) == null)
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnKickAvatarFromRoom(target, sender, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), path));
return;
}
if (!room.isMember(target)) {
player.sendPacket(new ChatOnKickAvatarFromRoom(target, sender, ChatResult.TARGET_AVATAR_DOESNT_EXIST.getCode(), path));
return;
}
Player targetPlayer = player.getPlayerManager().getPlayerByCreatureFirstName(target.getName());
if (targetPlayer == null) {
player.sendPacket(new ChatOnKickAvatarFromRoom(target, sender, ChatResult.TARGET_AVATAR_DOESNT_EXIST.getCode(), path));
return;
}
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnKickAvatarFromRoom(target, sender, ChatResult.SUCCESS.getCode(), path));
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnKickAvatarFromRoom(target, sender, result.getCode(), p.getRoom()));
}
private void handleChatUninviteFromRoom(Player player, ChatUninviteFromRoom p) {
String path = p.getRoom();
ChatRoom room = getRoom(path);
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar invitee = p.getAvatar();
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null) {
player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.ROOM_INVALID_NAME.getCode(), p.getSequence()));
return;
}
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (room.isPublic())
result = ChatResult.CUSTOM_FAILURE;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (!room.removeInvited(invitee))
result = ChatResult.ROOM_PRIVATE;
if (room.isPublic()) {
player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.CUSTOM_FAILURE.getCode(), p.getSequence()));
return;
}
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), p.getSequence()));
return;
}
if (!room.removeInvited(invitee)) {
player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.ROOM_PRIVATE.getCode(), p.getSequence()));
return;
}
player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.SUCCESS.getCode(), p.getSequence()));
player.sendPacket(new ChatOnUninviteFromRoom(p.getRoom(), sender, invitee, result.getCode(), p.getSequence()));
}
private void handleChatInviteToRoom(Player player, ChatInviteAvatarToRoom p) {
String path = p.getRoom();
ChatRoom room = getRoom(path);
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
if (room == null) {
player.sendPacket(new ChatOnInviteToRoom(path, sender, p.getAvatar(), ChatResult.ROOM_INVALID_NAME.getCode()));
return;
}
if (room.isPublic()) {
player.sendPacket(new ChatOnInviteToRoom(path, sender, p.getAvatar(), ChatResult.CUSTOM_FAILURE.getCode()));
return;
}
if (!room.isModerator(sender)) {
player.sendPacket(new ChatOnInviteToRoom(path, sender, p.getAvatar(), ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode()));
return;
}
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoom());
ChatAvatar invitee = p.getAvatar();
ChatAvatar sender = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if (room == null)
result = ChatResult.ROOM_INVALID_NAME;
else if (room.isPublic())
result = ChatResult.CUSTOM_FAILURE;
else if (!room.isModerator(sender))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
Player invitedPlayer = player.getPlayerManager().getPlayerByCreatureFirstName(invitee.getName());
if (invitedPlayer == null) {
player.sendPacket(new ChatOnInviteToRoom(path, sender, invitee, ChatResult.TARGET_AVATAR_DOESNT_EXIST.getCode()));
return;
if (result == ChatResult.SUCCESS && invitedPlayer == null)
result = ChatResult.TARGET_AVATAR_DOESNT_EXIST;
player.sendPacket(new ChatOnInviteToRoom(p.getRoom(), sender, invitee, result.getCode()));
if (result == ChatResult.SUCCESS) {
room.addInvited(invitee);
// Notify the invited client that the room exists if not already in the clients chat lists
invitedPlayer.sendPacket(new ChatRoomList(room));
invitedPlayer.sendPacket(new ChatOnReceiveRoomInvitation(sender, p.getRoom()));
}
player.sendPacket(new ChatOnInviteToRoom(path, sender, invitee, ChatResult.SUCCESS.getCode()));
room.addInvited(invitee);
// Notify the invited client that the room exists if not already in the clients chat lists
invitedPlayer.sendPacket(new ChatRoomList(room));
invitedPlayer.sendPacket(new ChatOnReceiveRoomInvitation(sender, path));
}
private void handleChatDestroyRoom(Player player, ChatDestroyRoom p) {
ChatRoom room = roomMap.get(p.getRoomId());
ChatRoom room = chatRoomHandler.getRoomById(p.getRoomId());
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
ChatResult result = ChatResult.SUCCESS;
if ((room == null || !room.getCreator().equals(avatar) || !room.getOwner().equals(avatar))) {
player.sendPacket(new ChatOnDestroyRoom(avatar, ChatResult.ROOM_AVATAR_NO_PERMISSION.getCode(), p.getRoomId(), p.getSequence()));
return;
}
if ((room == null || !room.getCreator().equals(avatar) || !room.getOwner().equals(avatar)))
result = ChatResult.ROOM_AVATAR_NO_PERMISSION;
else if (!chatRoomHandler.notifyDestroyRoom(avatar, room.getPath(), p.getSequence()))
result = ChatResult.NONE;
if (!notifyDestroyRoom(avatar, room.getPath(), p.getSequence())) {
player.sendPacket(new ChatOnDestroyRoom(avatar, ChatResult.NONE.getCode(), p.getRoomId(), p.getSequence()));
return;
}
player.sendPacket(new ChatOnDestroyRoom(avatar, ChatResult.SUCCESS.getCode(), p.getRoomId(), p.getSequence()));
player.sendPacket(new ChatOnDestroyRoom(avatar, result.getCode(), p.getRoomId(), p.getSequence()));
}
private void handleChatCreateRoom(Player player, ChatCreateRoom p) {
String path = p.getRoomName();
String title = p.getRoomTitle();
ChatRoom room = getRoom(path);
ChatResult result = ChatResult.SUCCESS;
if (room != null)
if (chatRoomHandler.getRoomByPath(path) != null)
result = ChatResult.ROOM_ALREADY_EXISTS;
else
chatRoomHandler.createRoom(ChatAvatar.getFromPlayer(player), p.isPublic(), p.isModerated(), path, p.getRoomTitle(), true);
if (result == ChatResult.SUCCESS) {
room = createRoom(ChatAvatar.getFromPlayer(player), p.isPublic(), path, title, true);
room.setModerated(p.isModerated());
}
player.sendPacket(new ChatOnCreateRoom(result.getCode(), room, p.getSequence()));
player.sendPacket(new ChatOnCreateRoom(result.getCode(), chatRoomHandler.getRoomByPath(path), p.getSequence()));
}
private void handleChatSendToRoom(Player player, ChatSendToRoom p) {
ChatRoom room = getRoom(p.getRoomId());
sendMessageToRoom(player, room, p.getSequence(), p.getMessage(), p.getOutOfBandPackage());
}
private void sendMessageToRoom(Player player, ChatRoom room, int sequence, String message, OutOfBandPackage outOfBandPackage) {
ChatResult result = ChatResult.SUCCESS;
if (room == null)
result = ChatResult.ROOM_INVALID_ID;
if (result != ChatResult.SUCCESS) {
player.sendPacket(new ChatOnSendRoomMessage(result.getCode(), sequence));
return;
}
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
result = room.canSendMessage(avatar);
// TODO: Check length of messages -- Result 16 is used for too long message
player.sendPacket(new ChatOnSendRoomMessage(result.getCode(), sequence));
if (result == ChatResult.SUCCESS) {
room.sendMessage(avatar, message, outOfBandPackage, player.getPlayerManager());
logChat(player.getCreatureObject().getObjectId(), player.getCharacterName(), room.getId() + "/" + room.getPath(), message);
}
chatRoomHandler.sendMessageToRoom(player, p.getRoomId(), p.getSequence(), p.getMessage(), p.getOutOfBandPackage());
}
private void handleChatQueryRoom(Player player, ChatQueryRoom p) {
ChatRoom room = getRoom(p.getRoomPath()); // No result code is sent for queries
ChatRoom room = chatRoomHandler.getRoomByPath(p.getRoomPath()); // No result code is sent for queries
if (room == null)
return;
@@ -560,291 +373,7 @@ public class ChatRoomService extends Service {
}
private void handleChatRoomListRequest(Player player) {
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
List<ChatRoom> rooms = new ArrayList<>();
for (ChatRoom chatRoom : roomMap.values()) {
if (!chatRoom.isPublic() && !chatRoom.isInvited(avatar) && !chatRoom.getOwner().equals(avatar))
continue;
rooms.add(chatRoom);
}
ChatRoomList response = new ChatRoomList(rooms);
player.sendPacket(response);
}
private void sendOnEnteredChatRoom(Player player, ChatAvatar avatar, ChatResult result, int id, int sequence) {
ChatOnEnteredRoom onEnteredRoom = new ChatOnEnteredRoom(avatar, id, sequence);
onEnteredRoom.setResult(result.getCode());
player.sendPacket(onEnteredRoom);
}
public void enterChatChannels(Player player, List<String> channels) {
for (String s : channels) {
enterChatChannel(player, s, false);
}
}
public void enterPlanetaryChatChannels(Player player) {
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return;
// Leave old zone-only chat channels
String planetEndPath = ".Planet";
for (String channel : new ArrayList<>(ghost.getJoinedChannels())) {
if (channel.endsWith(planetEndPath)) {
leaveChatChannel(player, channel);
} else {
// Better way of doing this?
String[] split = channel.split("\\.");
if (split.length == 3 && split[2].equals("system"))
leaveChatChannel(player, channel);
}
}
Terrain terrain = player.getCreatureObject().getTerrain();
// Enter the new zone-only chat channels
String planetPath = "SWG." + player.getGalaxyName() + "." + terrain.getName() + ".";
Assert.notNull(getRoom(planetPath + "Planet"));
Assert.notNull(getRoom(planetPath + "system"));
enterChatChannel(player, planetPath + "Planet", false);
enterChatChannel(player, planetPath + "system", false);
}
/**
* Attempts to join the specified chat channel
*
* @param player Player joining the chat channel
* @param room Chat room to enter
*/
public void enterChatChannel(Player player, ChatRoom room, int sequence, boolean ignoreInvitation) {
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
PlayerObject ghost = player.getPlayerObject();
if (ghost == null) {
sendOnEnteredChatRoom(player, avatar, ChatResult.NONE, room.getId(), sequence);
return;
}
ChatResult result = room.canJoinRoom(avatar, ignoreInvitation);
if (result != ChatResult.SUCCESS && player.getAccessLevel() != AccessLevel.PLAYER) {
sendOnEnteredChatRoom(player, avatar, result, room.getId(), sequence);
return;
}
// TODO: Check if player is appropriate faction for the room (Rebel and imperial chat rooms)
// Server-based list so we can join chat channels automatically
ghost.addJoinedChannel(room.getPath());
// Re-send the player the room list with just this room as it could have been private/hidden
// This also "refreshes" the client, not sending this will cause a Chat channel unavailable message.
if (!room.isPublic())
player.sendPacket(new ChatRoomList(room));
// Notify players of success, it's ChatResult.SUCCESS at this point
player.sendPacket(new ChatOnEnteredRoom(avatar, result.getCode(), room.getId(), sequence));
PlayerManager manager = player.getPlayerManager();
// Notify everyone that a player entered the room
room.sendPacketToMembers(manager, new ChatOnEnteredRoom(avatar, result.getCode(), room.getId(), 0));
room.addMember(avatar);
}
public void enterChatChannel(Player player, int id, int sequence) {
ChatRoom room = getRoom(id);
if (room == null) {
sendOnEnteredChatRoom(player, ChatAvatar.getFromPlayer(player), ChatResult.NONE, id, sequence);
return;
}
enterChatChannel(player, room, sequence, false);
}
public void enterChatChannel(Player player, String path, boolean ignoreInvitation) {
for (ChatRoom room : roomMap.values()) {
if (room.getPath().equals(path)) {
enterChatChannel(player, room, 0, ignoreInvitation);
return;
}
}
// Channel was not found, attempt to remove it from this players list of channels if it exists.
// This can happen if a channel was deleted while the player was offline
PlayerObject ghost = player.getPlayerObject();
if (ghost == null) {
Log.e("Tried to join a room with a path that does not exist: " + path);
return;
}
ghost.removeJoinedChannel(path);
}
public void leaveChatChannel(Player player, ChatRoom room, int sequence) {
ChatAvatar avatar = ChatAvatar.getFromPlayer(player);
PlayerObject ghost = player.getPlayerObject();
if (ghost == null)
return; // ChatOnLeaveRoom doesn't do anything other than for a ChatResult.SUCCESS, so no need to send a fail
if (!room.removeMember(avatar) && !ghost.removeJoinedChannel(room.getPath()))
return;
player.sendPacket(new ChatOnLeaveRoom(avatar, ChatResult.SUCCESS.getCode(), room.getId(), sequence));
room.sendPacketToMembers(player.getPlayerManager(), new ChatOnLeaveRoom(avatar, ChatResult.SUCCESS.getCode(), room.getId(), 0));
}
public void leaveChatChannel(Player player, String path) {
for (ChatRoom chatRoom : roomMap.values()) {
if (chatRoom.getPath().equals(path)) {
leaveChatChannel(player, chatRoom, 0);
break;
}
}
}
/**
* Creates a new chat room with the specified address path. If the path's parent channel doesn't exist, then a new chat room is created with the same passed arguments.
*
* @param creator Room creator who will also become the owner of this room
* @param isPublic Determines if the room should be publicly displayed in the channel listing
* @param path Address for the channel (Ex: SWG.serverName.Imperial)
* @param title Descriptive name of the chat channel (Ex: Imperial chat for this galaxy)
* @param persist If true then this channel will be saved in an {@link ObjectDatabase}
* @return {@link ChatRoom}
*/
public ChatRoom createRoom(ChatAvatar creator, boolean isPublic, String path, String title, boolean persist) {
if (path.isEmpty() || path.endsWith("."))
return null;
String base = "SWG." + creator.getGalaxy();
if (!path.startsWith(base) || path.equals(base))
return null;
if (getRoom(path) != null)
return getRoom(path);
// All paths should have parents, lets validate to make sure they exist first. Create them if they don't.
int lastIndex = path.lastIndexOf('.');
if (lastIndex != -1) {
String parentPath = path.substring(0, lastIndex);
if (getRoom(parentPath) == null) {
createRoom(creator, isPublic, parentPath, "", persist);
}
}
ChatRoom room = new ChatRoom();
room.setId(maxChatRoomId.incrementAndGet());
room.setOwner(creator);
room.setCreator(creator);
room.setIsPublic(isPublic);
room.setPath(path);
room.setTitle(title);
room.addModerator(creator);
roomMap.put(room.getId(), room);
if (persist)
database.add(room);
return room;
}
/**
* Creates a new, non-persistent, chat room with the specified address path.
*
* @param creator Room creator who will also become the owner of this room
* @param isPublic Determines if the room should be publicly displayed in the channel listing
* @param path Address for the channel (Ex: SWG.serverName.Imperial)
* @param title Descriptive name of the chat channel (Ex: Imperial chat for this galaxy)
* @return {@link ChatRoom}
*/
public ChatRoom createRoom(ChatAvatar creator, boolean isPublic, String path, String title) {
return createRoom(creator, isPublic, path, title, false);
}
public boolean notifyDestroyRoom(ChatAvatar destroyer, String roomPath, int sequence) {
ChatRoom room = getRoom(roomPath);
if (roomPath == null)
return false;
if (!destroyRoom(room))
return false;
// Send the ChatOnDestroyRoom packet to every else in the room besides the person destroying the packet
List<Long> networkIds = new ArrayList<>();
room.getMembers().forEach(member -> {
if (!destroyer.equals(member))
networkIds.add(member.getNetworkId());
});
new NotifyPlayersPacketIntent(new ChatOnDestroyRoom(destroyer, ChatResult.SUCCESS.getCode(), room.getId(), 0), networkIds).broadcast();
return true;
}
public boolean destroyRoom(ChatRoom room) {
return room != null && roomMap.remove(room.getId()) != null;
}
public void createSystemChannels(String galaxy) {
/**
* Channel Notes Group channels: created by System - SWG.serverName.group.GroupObjectId.GroupChat title = GroupId Guild channels: created by System - SWG.serverName.guild.GuildId.GuildChat title = GuildId City channels: created by System - SWG.serverName.city.CityId.CityChat title = CityId
*/
ChatAvatar systemAvatar = ChatAvatar.getSystemAvatar(galaxy);
String basePath = "SWG." + galaxy + ".";
DatatableData rooms = ServerFactory.getDatatable("chat/default_rooms.iff");
rooms.handleRows((r) -> createRoom(systemAvatar, true, basePath + rooms.getCell(r, 0), (String) rooms.getCell(r, 1), true));
createPlanetChannels(systemAvatar, basePath);
/*
* Battlefield Room path examples: SWG.Bria.corellia.battlefield SWG.Bria.corellia.battlefield.corellia_mountain_fortress.allFactions SWG.Bria.corellia.battlefield.corellia_pvp.allFactions / Imperial / Rebel SWG.Bria.corellia.battlefield.corellia_rebel_riverside_fort.allFactions
*/
}
private void createPlanetChannels(ChatAvatar systemAvatar, String basePath) {
DatatableData planets = ServerFactory.getDatatable("chat/planets.iff");
planets.handleRows((r) -> {
String path = basePath + planets.getCell(r, 0) + ".";
createRoom(systemAvatar, true, path + "Planet", "public chat for this planet, cannot create rooms here", true);
createRoom(systemAvatar, true, path + "system", "system messages for this planet, cannot create rooms here", true);
createRoom(systemAvatar, true, path + "Chat", "public chat for this planet, can create rooms here", true);
});
}
public ChatRoom getRoom(String path) {
for (ChatRoom room : roomMap.values()) {
if (room.getPath().equals(path))
return room;
}
return null;
}
public ChatRoom getRoom(int roomId) {
return roomMap.get(roomId);
}
private void logChat(long sendId, String sendName, String room, String message) {
try {
synchronized (insertChatLog) {
insertChatLog.setLong(1, System.currentTimeMillis());
insertChatLog.setLong(2, sendId);
insertChatLog.setString(3, sendName);
insertChatLog.setLong(4, 0);
insertChatLog.setString(5, "");
insertChatLog.setString(6, ChatType.CHAT.name());
insertChatLog.setString(7, ChatRange.ROOM.name());
insertChatLog.setString(8, room);
insertChatLog.setString(9, "");
insertChatLog.setString(10, message);
insertChatLog.executeUpdate();
}
} catch (SQLException e) {
Log.e(e);
}
player.sendPacket(new ChatRoomList(chatRoomHandler.getRoomList(player)));
}
}