mirror of
https://github.com/ProjectSWGCore/Holocore.git
synced 2026-10-18 08:02:56 -04:00
358 lines
12 KiB
Java
358 lines
12 KiB
Java
package services.chat;
|
|
|
|
import java.util.Date;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import intents.GalacticPacketIntent;
|
|
import intents.NotifyPlayersPacketIntent;
|
|
import intents.PlayerEventIntent;
|
|
import intents.ServerStatusIntent;
|
|
import intents.ServerStatusIntent.ServerStatus;
|
|
import intents.chat.ChatBroadcastIntent;
|
|
import intents.chat.PersistentMessageIntent;
|
|
import intents.chat.SpatialChatIntent;
|
|
import network.packets.Packet;
|
|
import network.packets.swg.SWGPacket;
|
|
import network.packets.swg.zone.ChatRequestRoomList;
|
|
import network.packets.swg.zone.chat.ChatDeletePersistentMessage;
|
|
import network.packets.swg.zone.chat.ChatInstantMessageToCharacter;
|
|
import network.packets.swg.zone.chat.ChatInstantMessageToClient;
|
|
import network.packets.swg.zone.chat.ChatOnSendInstantMessage;
|
|
import network.packets.swg.zone.chat.ChatOnSendPersistentMessage;
|
|
import network.packets.swg.zone.chat.ChatPersistentMessageToClient;
|
|
import network.packets.swg.zone.chat.ChatPersistentMessageToServer;
|
|
import network.packets.swg.zone.chat.ChatRequestPersistentMessage;
|
|
import network.packets.swg.zone.chat.ChatSystemMessage;
|
|
import network.packets.swg.zone.chat.ChatSystemMessage.SystemChatType;
|
|
import network.packets.swg.zone.object_controller.ObjectController;
|
|
import network.packets.swg.zone.object_controller.SpatialChat;
|
|
import resources.Terrain;
|
|
import resources.control.Intent;
|
|
import resources.control.Service;
|
|
import resources.encodables.OutOfBand;
|
|
import resources.encodables.ProsePackage;
|
|
import resources.encodables.player.Mail;
|
|
import resources.objects.SWGObject;
|
|
import resources.player.Player;
|
|
import resources.server_info.CachedObjectDatabase;
|
|
import resources.server_info.ObjectDatabase;
|
|
import resources.server_info.ObjectDatabase.Traverser;
|
|
import services.player.PlayerManager;
|
|
|
|
public class ChatService extends Service {
|
|
|
|
private ObjectDatabase<Mail> mails;
|
|
private int maxMailId;
|
|
|
|
public ChatService() {
|
|
mails = new CachedObjectDatabase<Mail>("odb/mails.db");
|
|
maxMailId = 1;
|
|
}
|
|
|
|
@Override
|
|
public boolean initialize() {
|
|
registerForIntent(GalacticPacketIntent.TYPE);
|
|
registerForIntent(SpatialChatIntent.TYPE);
|
|
registerForIntent(PersistentMessageIntent.TYPE);
|
|
registerForIntent(PlayerEventIntent.TYPE);
|
|
registerForIntent(ChatBroadcastIntent.TYPE);
|
|
registerForIntent(ServerStatusIntent.TYPE);
|
|
mails.load();
|
|
mails.traverse(new Traverser<Mail>() {
|
|
@Override
|
|
public void process(Mail mail) {
|
|
if (mail.getId() >= maxMailId)
|
|
maxMailId = mail.getId() + 1;
|
|
}
|
|
});
|
|
return super.initialize();
|
|
}
|
|
|
|
public void onIntentReceived(Intent i) {
|
|
switch (i.getType()) {
|
|
case GalacticPacketIntent.TYPE:
|
|
if (i instanceof GalacticPacketIntent)
|
|
processPacket((GalacticPacketIntent) i);
|
|
break;
|
|
case SpatialChatIntent.TYPE:
|
|
if (i instanceof SpatialChatIntent)
|
|
handleSpatialChat((SpatialChatIntent) i);
|
|
break;
|
|
case PersistentMessageIntent.TYPE:
|
|
if (i instanceof PersistentMessageIntent)
|
|
handlePersistentMessageIntent((PersistentMessageIntent) i);
|
|
break;
|
|
case PlayerEventIntent.TYPE:
|
|
if (i instanceof PlayerEventIntent)
|
|
handlePlayerEventIntent((PlayerEventIntent) i);
|
|
break;
|
|
case ChatBroadcastIntent.TYPE:
|
|
if (i instanceof ChatBroadcastIntent)
|
|
handleChatBroadcast((ChatBroadcastIntent) i);
|
|
break;
|
|
case ServerStatusIntent.TYPE:
|
|
if (i instanceof ServerStatusIntent)
|
|
if (((ServerStatusIntent)i).getStatus() == ServerStatus.SHUTDOWN_REQUESTED)
|
|
sendShutdownBroadcasts(((ServerStatusIntent) i).getTime());
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void processPacket(GalacticPacketIntent intent) {
|
|
Player player = intent.getPlayerManager().getPlayerFromNetworkId(intent.getNetworkId());
|
|
if (player == null)
|
|
return;
|
|
Packet p = intent.getPacket();
|
|
if (p instanceof SWGPacket)
|
|
processSwgPacket(intent.getPlayerManager(), player, intent.getGalaxy().getName(), (SWGPacket) p);
|
|
}
|
|
|
|
private void processSwgPacket(PlayerManager pm, Player player, String galaxyName, SWGPacket p) {
|
|
switch (p.getPacketType()) {
|
|
case CHAT_REQUEST_ROOM_LIST:
|
|
if (p instanceof ChatRequestRoomList)
|
|
handleChatRoomListRequest(player, (ChatRequestRoomList) p);
|
|
break;
|
|
case CHAT_INSTANT_MESSAGE_TO_CHARACTER:
|
|
if (p instanceof ChatInstantMessageToCharacter)
|
|
handleInstantMessage(pm, player, (ChatInstantMessageToCharacter) p);
|
|
break;
|
|
case CHAT_PERSISTENT_MESSAGE_TO_SERVER:
|
|
if (p instanceof ChatPersistentMessageToServer)
|
|
handleSendPersistentMessage(pm, player, galaxyName, (ChatPersistentMessageToServer) p);
|
|
break;
|
|
case CHAT_REQUEST_PERSISTENT_MESSAGE:
|
|
if (p instanceof ChatRequestPersistentMessage)
|
|
handlePersistentMessageRequest(player, galaxyName, (ChatRequestPersistentMessage) p);
|
|
break;
|
|
case CHAT_DELETE_PERSISTENT_MESSAGE:
|
|
if (p instanceof ChatDeletePersistentMessage)
|
|
deletePersistentMessage(((ChatDeletePersistentMessage) p).getMailId());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void handlePlayerEventIntent(PlayerEventIntent intent) {
|
|
switch (intent.getEvent()) {
|
|
case PE_ZONE_IN:
|
|
sendPersistentMessageHeaders(intent.getPlayer(), intent.getGalaxy());
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
private void handleChatBroadcast(ChatBroadcastIntent i) {
|
|
switch(i.getBroadcastType()) {
|
|
case AREA:
|
|
broadcastAreaMessage(i.getMessage(), i.getBroadcaster());
|
|
break;
|
|
case PLANET:
|
|
broadcastGalaxyMessage(i.getMessage(), i.getTerrain()); // TODO: Planet specific system messages
|
|
break;
|
|
case GALAXY:
|
|
broadcastGalaxyMessage(i.getMessage(), i.getTerrain());
|
|
break;
|
|
case PERSONAL:
|
|
broadcastPersonalMessage(i.getProse(), i.getBroadcaster());
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void handleChatRoomListRequest(Player player, ChatRequestRoomList request) {
|
|
|
|
}
|
|
|
|
private void handleSpatialChat(SpatialChatIntent i) {
|
|
Player sender = i.getPlayer();
|
|
SWGObject actor = sender.getCreatureObject();
|
|
|
|
// Send to self
|
|
SpatialChat message = new SpatialChat(actor.getObjectId(), 0, i.getMessage(), i.getChatType(), i.getMoodId());
|
|
sender.sendPacket(new ObjectController(SpatialChat.CRC, actor.getObjectId(), message));
|
|
|
|
// Notify observers of the chat message
|
|
for (Player observer : actor.getObservers()) {
|
|
if (observer.getCreatureObject() == null)
|
|
continue;
|
|
long id = observer.getCreatureObject().getObjectId();
|
|
observer.sendPacket(new ObjectController(SpatialChat.CRC, id, message));
|
|
}
|
|
}
|
|
|
|
private void handleInstantMessage(PlayerManager playerMgr, Player sender, ChatInstantMessageToCharacter request) {
|
|
String strReceiver = request.getCharacter().toLowerCase(Locale.ENGLISH);
|
|
String strSender = sender.getCharacterName();
|
|
|
|
if (strSender.contains(" "))
|
|
strSender = strSender.split(" ")[0];
|
|
|
|
Player receiver = playerMgr.getPlayerByCreatureFirstName(strReceiver);
|
|
|
|
int errorCode = 0; // 0 = No issue, 4 = "strReceiver is not online"
|
|
if (receiver == null)
|
|
errorCode = 4;
|
|
|
|
sender.sendPacket(new ChatOnSendInstantMessage(errorCode, request.getSequence()));
|
|
|
|
if (receiver == null)
|
|
return;
|
|
|
|
receiver.sendPacket(new ChatInstantMessageToClient(request.getGalaxy(), strSender, request.getMessage()));
|
|
}
|
|
|
|
private void handleSendPersistentMessage(PlayerManager playerMgr, Player sender, String galaxy, ChatPersistentMessageToServer request) {
|
|
String recipientStr = request.getRecipient().toLowerCase(Locale.ENGLISH);
|
|
|
|
if (recipientStr.contains(" "))
|
|
recipientStr = recipientStr.split(" ")[0];
|
|
|
|
Player recipient = playerMgr.getPlayerByCreatureFirstName(recipientStr);
|
|
long recId = (recipient == null ? playerMgr.getCharacterIdByName(request.getRecipient()) : recipient.getCreatureObject().getObjectId());
|
|
int errorCode = 0;
|
|
|
|
if (recId == 0)
|
|
errorCode = 4;
|
|
|
|
sender.sendPacket(new ChatOnSendPersistentMessage(errorCode, request.getCounter()));
|
|
|
|
Mail mail = new Mail(sender.getCharacterName(), request.getSubject(), request.getMessage(), recId);
|
|
mail.setId(maxMailId++);
|
|
mail.setTimestamp((int) (new Date().getTime() / 1000));
|
|
|
|
mails.put(mail.getId(), mail);
|
|
|
|
if (recipient != null)
|
|
sendPersistentMessage(recipient, mail, MailFlagType.HEADER_ONLY, galaxy);
|
|
}
|
|
|
|
private void handlePersistentMessageIntent(PersistentMessageIntent intent) {
|
|
if (intent.getReceiver() == null)
|
|
return;
|
|
|
|
Player recipient = intent.getReceiver().getOwner();
|
|
|
|
if (recipient == null)
|
|
return;
|
|
|
|
Mail mail = intent.getMail();
|
|
mail.setId(maxMailId);
|
|
maxMailId++;
|
|
mail.setTimestamp((int) (new Date().getTime() / 1000));
|
|
|
|
mails.put(mail.getId(), mail);
|
|
|
|
sendPersistentMessage(recipient, mail, MailFlagType.HEADER_ONLY, intent.getGalaxy());
|
|
}
|
|
|
|
private void handlePersistentMessageRequest(Player player, String galaxy, ChatRequestPersistentMessage request) {
|
|
Mail mail = mails.get(request.getMailId());
|
|
|
|
if (mail == null)
|
|
return;
|
|
|
|
if (mail.getReceiverId() != player.getCreatureObject().getObjectId())
|
|
return;
|
|
|
|
sendPersistentMessage(player, mail, MailFlagType.FULL_MESSAGE, galaxy);
|
|
}
|
|
|
|
private void broadcastAreaMessage(String message, Player broadcaster) {
|
|
ChatSystemMessage packet = new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT.ordinal(), message);
|
|
broadcaster.sendPacket(packet);
|
|
|
|
List<Player> observers = broadcaster.getCreatureObject().getObservers();
|
|
for (Player player : observers) {
|
|
player.sendPacket(packet);
|
|
}
|
|
}
|
|
|
|
private void broadcastGalaxyMessage(String message, Terrain terrain) {
|
|
ChatSystemMessage packet = new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT.ordinal(), message);
|
|
new NotifyPlayersPacketIntent(packet, terrain).broadcast();
|
|
}
|
|
|
|
private void broadcastPersonalMessage(ProsePackage prose, Player player) {
|
|
OutOfBand pckg = new OutOfBand(prose);
|
|
player.sendPacket(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT, pckg));
|
|
}
|
|
|
|
private void sendShutdownBroadcasts(final long time) {
|
|
final AtomicInteger runCount = new AtomicInteger();
|
|
final long interval = (int) (time / 3);
|
|
|
|
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() {
|
|
public void run() {
|
|
switch(runCount.getAndIncrement()) {
|
|
case 0:
|
|
broadcastGalaxyMessage(String.format("The server will be shutting down in %d minutes.", time), null);
|
|
break;
|
|
case 1:
|
|
broadcastGalaxyMessage(String.format("The server will be shutting down in %d minutes.", time - interval), null);
|
|
break;
|
|
case 2:
|
|
broadcastGalaxyMessage(String.format("The server will be shutting down in %d minutes.", time - (interval*2)), null);
|
|
break;
|
|
case 3:
|
|
broadcastGalaxyMessage("The server will now be shutting down.", null);
|
|
throw new RuntimeException("Reached max runCount"); // no "clean" ways to cancel the runnable I can think of
|
|
}
|
|
}
|
|
}, 0, interval, TimeUnit.MINUTES);
|
|
|
|
}
|
|
|
|
private void sendPersistentMessageHeaders(Player player, String galaxy) {
|
|
if (player == null || player.getCreatureObject() == null)
|
|
return;
|
|
|
|
final List <Mail> playersMail = new LinkedList<Mail>();
|
|
final long receiverId = player.getCreatureObject().getObjectId();
|
|
|
|
mails.traverse(new Traverser<Mail>() {
|
|
@Override
|
|
public void process(Mail element) {
|
|
if (element.getReceiverId() == receiverId)
|
|
playersMail.add(element);
|
|
}
|
|
});
|
|
|
|
for (Mail mail : playersMail)
|
|
sendPersistentMessage(player, mail, MailFlagType.HEADER_ONLY, galaxy);
|
|
}
|
|
|
|
private void sendPersistentMessage(Player receiver, Mail mail, MailFlagType requestType, String galaxy) {
|
|
if (receiver == null || receiver.getCreatureObject() == null)
|
|
return;
|
|
|
|
ChatPersistentMessageToClient packet = null;
|
|
|
|
switch (requestType) {
|
|
case FULL_MESSAGE:
|
|
packet = new ChatPersistentMessageToClient((byte) 0, mail.getSender(), galaxy, mail.getId(), mail.getSubject(), mail.getMessage(), mail.getTimestamp(), mail.getStatus());
|
|
break;
|
|
case HEADER_ONLY:
|
|
default:
|
|
packet = new ChatPersistentMessageToClient((byte) 1, mail.getSender(), galaxy, mail.getId(), mail.getSubject(), "", mail.getTimestamp(), mail.getStatus());
|
|
break;
|
|
}
|
|
|
|
receiver.sendPacket(packet);
|
|
}
|
|
|
|
private void deletePersistentMessage(int mailId) {
|
|
mails.remove(mailId);
|
|
}
|
|
|
|
private enum MailFlagType {
|
|
FULL_MESSAGE,
|
|
HEADER_ONLY;
|
|
}
|
|
}
|