diff --git a/src/main/ProjectSWG.java b/src/main/ProjectSWG.java index e0ae16ea3..2510a3b07 100644 --- a/src/main/ProjectSWG.java +++ b/src/main/ProjectSWG.java @@ -31,13 +31,10 @@ import intents.server.ServerStatusIntent; import resources.Galaxy.GalaxyStatus; import resources.control.IntentManager; import resources.control.ServerStatus; -import resources.server_info.Log; import services.CoreManager; public class ProjectSWG { - private static final String [] ENDINGS = {"B", "KB", "MB", "GB"}; - private static ProjectSWG server; private final Thread mainThread; private CoreManager manager; @@ -135,7 +132,6 @@ public class ProjectSWG { private void loop() { setStatus((manager.getGalaxyStatus() == GalaxyStatus.UP) ? ServerStatus.OPEN : ServerStatus.LOCKED); - long iter = 0; while (!shutdownRequested && !manager.isShutdownRequested() && manager.isOperational()) { manager.flushPackets(); // Sends any packets that weren't sent try { @@ -143,25 +139,9 @@ public class ProjectSWG { } catch (InterruptedException e) { throw new CoreException("Main Thread Interrupted."); } - iter++; - if (iter % 20 == 0) { - final Runtime r = Runtime.getRuntime(); - Log.i("ProjectSWG", "Memory Consumption: %s", getMemory((r.totalMemory()-r.freeMemory()))); - } } } - private String getMemory(long bytes) { - double val = bytes; - int iters = 0; - while (val >= 1024) { - val /= 1024; - iters++; - } - val = ((int) (val * 1E5)) / 1E5; - return val + ENDINGS[iters]; - } - private void stop() { if (manager == null || status == ServerStatus.OFFLINE) return; diff --git a/src/network/OutboundNetworkHandler.java b/src/network/OutboundNetworkHandler.java index 97788d1d3..31bbf1759 100644 --- a/src/network/OutboundNetworkHandler.java +++ b/src/network/OutboundNetworkHandler.java @@ -56,6 +56,8 @@ public class OutboundNetworkHandler { } public synchronized void reset() { + sequenced.clear(); + assembleQueue.clear(); sendSequence = 0; crc = 0; } @@ -91,7 +93,7 @@ public class OutboundNetworkHandler { while (it.hasNext()) { SequencedPacket sp = it.next(); if (sp.getSequence() <= sequence) - pushAssembledEncrypted(sp.getPacket()); + pushAssembledUnencrypted(sp.getPacket()); // Pre-encrypted before putting into list else break; } @@ -110,11 +112,12 @@ public class OutboundNetworkHandler { } } - private void pushAssembledEncrypted(byte [] data) { + private byte [] pushAssembledEncrypted(byte [] data) { data = Encryption.encode(data, crc); synchronized (assembleQueue) { assembleQueue.add(data); } + return data; } private void pushAssembledUnencrypted(byte [] data) { @@ -164,8 +167,8 @@ public class OutboundNetworkHandler { int lastSeq = updateSequencesMulti((short)(sendSequence+count), m); for (Fragmented f : Fragmented.encode(m.encode(), sendSequence)) { byte [] encoded = f.encode().array(); - pushSequencedPacket(f.getSequence(), encoded); - pushAssembledEncrypted(encoded); + byte [] encrypted = pushAssembledEncrypted(encoded); + pushSequencedPacket(f.getSequence(), encrypted); } sendSequence = (short) (lastSeq + 1); return count; @@ -183,16 +186,16 @@ public class OutboundNetworkHandler { int lastSeq = updateSequenceData((short)(sendSequence+count), d); for (Fragmented f : Fragmented.encode(d.encode(), sendSequence)) { byte [] encoded = f.encode().array(); - pushSequencedPacket(f.getSequence(), encoded); - pushAssembledEncrypted(encoded); + byte [] encrypted = pushAssembledEncrypted(encoded); + pushSequencedPacket(f.getSequence(), encrypted); } sendSequence = (short) lastSeq; return count; } else { d.setSequence(sendSequence++); byte [] encoded = d.encode().array(); - pushSequencedPacket(d.getSequence(), encoded); - pushAssembledEncrypted(encoded); + byte [] encrypted = pushAssembledEncrypted(encoded); + pushSequencedPacket(d.getSequence(), encrypted); return 1; } } diff --git a/src/network/PacketType.java b/src/network/PacketType.java index 1fef585c6..657c64edd 100644 --- a/src/network/PacketType.java +++ b/src/network/PacketType.java @@ -138,6 +138,8 @@ public enum PacketType { CHAT_KICK_AVATAR_FROM_ROOM (ChatKickAvatarFromRoom.CRC, ChatKickAvatarFromRoom.class), CHAT_BAN_AVATAR_FROM_ROOM (ChatBanAvatarFromRoom.CRC, ChatBanAvatarFromRoom.class), CHAT_UNBAN_AVATAR_FROM_ROOM (ChatUnbanAvatarFromRoom.CRC, ChatUnbanAvatarFromRoom.class), + CHAT_ADD_MODERATOR_TO_ROOM (ChatAddModeratorToRoom.CRC, ChatAddModeratorToRoom.class), + CHAT_REMOVE_MODERATOR_FROM_ROOM (ChatRemoveModeratorFromRoom.CRC, ChatRemoveModeratorFromRoom.class), CON_GENERIC_MESSAGE (ConGenericMessage.CRC, ConGenericMessage.class), VOICE_CHAT_STATUS (VoiceChatStatus.CRC, VoiceChatStatus.class), diff --git a/src/network/encryption/Encryption.java b/src/network/encryption/Encryption.java index 73fe79230..7c7334b96 100644 --- a/src/network/encryption/Encryption.java +++ b/src/network/encryption/Encryption.java @@ -39,11 +39,21 @@ public class Encryption { } public static byte [] encode(byte [] input, int crc) { - return assembleMessage(input, crc); + try { + return assembleMessage(input, crc); + } catch (Throwable t) { + t.printStackTrace(); + return new byte[0]; + } } public static byte [] decode(byte [] input, int crc) { - return disassemble(input, crc); + try { + return disassemble(input, crc); + } catch (Throwable t) { + t.printStackTrace(); + return new byte[0]; + } } private static byte [] disassemble(byte [] data, int crcSeed) { @@ -92,7 +102,12 @@ public class Encryption { Deflater compressor = new Deflater(); compressor.setInput(data, 2, data.length - 2); compressor.finish(); - int length = compressor.deflate(result); + int length = 0; + try { + length = compressor.deflate(result); + } finally { + compressor.end(); + } if (length < data.length) { ByteBuffer bb = ByteBuffer.allocate(length+3); bb.put(data[0]).put(data[1]); @@ -132,11 +147,12 @@ public class Encryption { try { int length = decompressor.inflate(result); - decompressor.end(); return length; } catch (DataFormatException e) { System.err.println("Failed to decompress packet. "+e.getClass().getSimpleName()+" Message: " + e.getMessage()); return -1; + } finally { + decompressor.end(); } } diff --git a/src/network/packets/swg/zone/chat/ChatAddModeratorToRoom.java b/src/network/packets/swg/zone/chat/ChatAddModeratorToRoom.java new file mode 100644 index 000000000..cecc2b43a --- /dev/null +++ b/src/network/packets/swg/zone/chat/ChatAddModeratorToRoom.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package network.packets.swg.zone.chat; + +import network.packets.swg.SWGPacket; +import resources.chat.ChatAvatar; + +import java.nio.ByteBuffer; + +/** + * @author Waverunner + */ +public class ChatAddModeratorToRoom extends SWGPacket { + public static final int CRC = getCrc("ChatAddModeratorToRoom"); + + private ChatAvatar avatar; + private String room; + private int sequence; + + @Override + public void decode(ByteBuffer data) { + if (!super.decode(data, CRC)) + return; + avatar = getEncodable(data, ChatAvatar.class); + room = getAscii(data); + sequence = getInt(data); + } + + @Override + public ByteBuffer encode() { + ByteBuffer bb = ByteBuffer.allocate(12 + room.length() + avatar.encode().length); + addShort(bb, 4); + addInt(bb, CRC); + addEncodable(bb, avatar); + addAscii(bb, room); + addInt(bb, sequence); + return bb; + } + + public ChatAvatar getAvatar() { + return avatar; + } + + public String getRoom() { + return room; + } + + public int getSequence() { + return sequence; + } +} diff --git a/src/network/packets/swg/zone/chat/ChatOnAddModeratorToRoom.java b/src/network/packets/swg/zone/chat/ChatOnAddModeratorToRoom.java new file mode 100644 index 000000000..620c7cfff --- /dev/null +++ b/src/network/packets/swg/zone/chat/ChatOnAddModeratorToRoom.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package network.packets.swg.zone.chat; + +import network.packets.swg.SWGPacket; +import resources.chat.ChatAvatar; + +import java.nio.ByteBuffer; + +/** + * @author Waverunner + */ +public class ChatOnAddModeratorToRoom extends SWGPacket { + public static final int CRC = getCrc("ChatOnAddModeratorToRoom"); + + private ChatAvatar target; + private ChatAvatar moderator; + private int result; + private String room; + private int sequence; + + public ChatOnAddModeratorToRoom(ChatAvatar target, ChatAvatar moderator, int result, String room, int sequence) { + this.target = target; + this.moderator = moderator; + this.result = result; + this.room = room; + this.sequence = sequence; + } + + @Override + public void decode(ByteBuffer data) { + if (!super.decode(data, CRC)) + return; + target = getEncodable(data, ChatAvatar.class); + moderator = getEncodable(data, ChatAvatar.class); + result = getInt(data); + room = getAscii(data); + sequence = getInt(data); + } + + @Override + public ByteBuffer encode() { + ByteBuffer bb = ByteBuffer.allocate(16 + target.encode().length + moderator.encode().length + room.length()); + addShort(bb, 1); + addInt(bb, CRC); + addEncodable(bb, target); + addEncodable(bb, moderator); + addInt(bb, result); + addAscii(bb, room); + addInt(bb, sequence); + return bb; + } +} diff --git a/src/network/packets/swg/zone/chat/ChatOnRemoveModeratorFromRoom.java b/src/network/packets/swg/zone/chat/ChatOnRemoveModeratorFromRoom.java new file mode 100644 index 000000000..5d559b9ab --- /dev/null +++ b/src/network/packets/swg/zone/chat/ChatOnRemoveModeratorFromRoom.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package network.packets.swg.zone.chat; + +import network.packets.swg.SWGPacket; +import resources.chat.ChatAvatar; + +import java.nio.ByteBuffer; + +/** + * @author Waverunner + */ +public class ChatOnRemoveModeratorFromRoom extends SWGPacket { + public static final int CRC = getCrc("ChatOnRemoveModeratorFromRoom"); + + private ChatAvatar target; + private ChatAvatar moderator; + private int result; + private String room; + private int sequence; + + public ChatOnRemoveModeratorFromRoom(ChatAvatar target, ChatAvatar moderator, int result, String room, int sequence) { + this.target = target; + this.moderator = moderator; + this.result = result; + this.room = room; + this.sequence = sequence; + } + + @Override + public void decode(ByteBuffer data) { + if (!super.decode(data, CRC)) + return; + target = getEncodable(data, ChatAvatar.class); + moderator = getEncodable(data, ChatAvatar.class); + result = getInt(data); + room = getAscii(data); + sequence = getInt(data); + } + + @Override + public ByteBuffer encode() { + ByteBuffer bb = ByteBuffer.allocate(16 + target.encode().length + moderator.encode().length + room.length()); + addShort(bb, 1); + addInt(bb, CRC); + addEncodable(bb, target); + addEncodable(bb, moderator); + addInt(bb, result); + addAscii(bb, room); + addInt(bb, sequence); + return bb; + } +} diff --git a/src/network/packets/swg/zone/chat/ChatRemoveModeratorFromRoom.java b/src/network/packets/swg/zone/chat/ChatRemoveModeratorFromRoom.java new file mode 100644 index 000000000..25204eb32 --- /dev/null +++ b/src/network/packets/swg/zone/chat/ChatRemoveModeratorFromRoom.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ + +package network.packets.swg.zone.chat; + +import network.packets.swg.SWGPacket; +import resources.chat.ChatAvatar; + +import java.nio.ByteBuffer; + +/** + * @author Waverunner + */ +public class ChatRemoveModeratorFromRoom extends SWGPacket { + public static final int CRC = getCrc("ChatRemoveModeratorFromRoom"); + + private ChatAvatar avatar; + private String room; + private int sequence; + + @Override + public void decode(ByteBuffer data) { + if (!super.decode(data, CRC)) + return; + avatar = getEncodable(data, ChatAvatar.class); + room = getAscii(data); + sequence = getInt(data); + } + + @Override + public ByteBuffer encode() { + ByteBuffer bb = ByteBuffer.allocate(12 + room.length() + avatar.encode().length); + addShort(bb, 4); + addInt(bb, CRC); + addEncodable(bb, avatar); + addAscii(bb, room); + addInt(bb, sequence); + return bb; + } + + public ChatAvatar getAvatar() { + return avatar; + } + + public String getRoom() { + return room; + } + + public int getSequence() { + return sequence; + } +} diff --git a/src/resources/chat/ChatResult.java b/src/resources/chat/ChatResult.java index 764dfaede..fa237e24e 100644 --- a/src/resources/chat/ChatResult.java +++ b/src/resources/chat/ChatResult.java @@ -36,7 +36,7 @@ public enum ChatResult { TARGET_AVATAR_DOESNT_EXIST(4), ROOM_INVALID_ID(5), ROOM_INVALID_NAME(6), - ROOM_NOT_MODERATOR(9), + CUSTOM_FAILURE(9), ROOM_AVATAR_BANNED(12), ROOM_PRIVATE(13), ROOM_AVATAR_NO_PERMISSION(16), diff --git a/src/resources/chat/ChatRoom.java b/src/resources/chat/ChatRoom.java index 948d3ce33..63596a7e3 100644 --- a/src/resources/chat/ChatRoom.java +++ b/src/resources/chat/ChatRoom.java @@ -32,12 +32,16 @@ import network.packets.swg.SWGPacket; import network.packets.swg.zone.chat.ChatRoomMessage; import resources.encodables.Encodable; import resources.encodables.OutOfBandPackage; +import resources.objects.SWGObject; import resources.player.Player; import services.player.PlayerManager; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; /** @@ -54,7 +58,7 @@ public class ChatRoom implements Encodable, Serializable { private String title; private List moderators; private List invited; - private boolean muted; // No one but moderators can talk + private boolean moderated; // No one but moderators can talk private List banned; // 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 @@ -72,7 +76,12 @@ public class ChatRoom implements Encodable, Serializable { members = new ArrayList<>(); banned = new ArrayList<>(); } - + + private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { + members = new ArrayList<>(); + ois.defaultReadObject(); + } + public int getId() { return id; } @@ -129,12 +138,12 @@ public class ChatRoom implements Encodable, Serializable { return invited; } - public boolean isMuted() { - return muted; + public boolean isModerated() { + return moderated; } - public void setMuted(boolean muted) { - this.muted = muted; + public void setModerated(boolean moderated) { + this.moderated = moderated; } public List getMembers() { @@ -173,8 +182,8 @@ public class ChatRoom implements Encodable, Serializable { if (banned.contains(avatar)) return ChatResult.ROOM_AVATAR_BANNED; - if (muted && !moderators.contains(avatar)) - return ChatResult.ROOM_NOT_MODERATOR; + if (moderated && !moderators.contains(avatar)) + return ChatResult.CUSTOM_FAILURE; return ChatResult.SUCCESS; } @@ -210,7 +219,7 @@ public class ChatRoom implements Encodable, Serializable { public void decode(ByteBuffer data) { id = Packet.getInt(data); type = Packet.getInt(data); - muted = Packet.getBoolean(data); + moderated = Packet.getBoolean(data); path = Packet.getAscii(data); owner = Packet.getEncodable(data, ChatAvatar.class); creator = Packet.getEncodable(data, ChatAvatar.class); @@ -237,7 +246,7 @@ public class ChatRoom implements Encodable, Serializable { ByteBuffer bb = ByteBuffer.allocate(23 + path.length() + (title.length() * 2) + avatarIdSize); Packet.addInt(bb, id); Packet.addInt(bb, type); - Packet.addBoolean(bb, muted); + Packet.addBoolean(bb, moderated); Packet.addAscii(bb, path); Packet.addEncodable(bb, owner); Packet.addEncodable(bb, creator); @@ -254,6 +263,6 @@ public class ChatRoom implements Encodable, Serializable { @Override public String toString() { return "ChatRoom[id=" + id + ", type=" + type + ", path='" + path + "', title='" + title + '\'' + - ", creator=" + creator + ", muted=" + muted + ", isPublic=" + isPublic() + "]"; + ", creator=" + creator + ", moderated=" + moderated + ", isPublic=" + isPublic() + "]"; } } diff --git a/src/resources/commands/callbacks/PlayerAppearanceCallback.java b/src/resources/commands/callbacks/PlayerAppearanceCallback.java new file mode 100644 index 000000000..54a835272 --- /dev/null +++ b/src/resources/commands/callbacks/PlayerAppearanceCallback.java @@ -0,0 +1,22 @@ +package resources.commands.callbacks; + +import resources.commands.ICmdCallback; +import resources.objects.SWGObject; +import resources.objects.creature.CreatureObject; +import resources.player.Player; +import services.galaxy.GalacticManager; + +public class PlayerAppearanceCallback implements ICmdCallback { + + @Override + public void execute(GalacticManager galacticManager, Player player, + SWGObject target, String args) { + if(target instanceof CreatureObject) { + CreatureObject creatureTarget = (CreatureObject) target; + + creatureTarget.setCostume(args); + } + + } + +} diff --git a/src/resources/commands/callbacks/RevertAppearanceCallback.java b/src/resources/commands/callbacks/RevertAppearanceCallback.java new file mode 100644 index 000000000..bfa5e498d --- /dev/null +++ b/src/resources/commands/callbacks/RevertAppearanceCallback.java @@ -0,0 +1,22 @@ +package resources.commands.callbacks; + +import resources.commands.ICmdCallback; +import resources.objects.SWGObject; +import resources.objects.creature.CreatureObject; +import resources.player.Player; +import services.galaxy.GalacticManager; + +public class RevertAppearanceCallback implements ICmdCallback { + + @Override + public void execute(GalacticManager galacticManager, Player player, + SWGObject target, String args) { + if(target instanceof CreatureObject) { + CreatureObject creatureTarget = (CreatureObject) target; + + creatureTarget.setCostume(""); + } + + } + +} diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java index a670e8163..afb39d04b 100644 --- a/src/resources/objects/SWGObject.java +++ b/src/resources/objects/SWGObject.java @@ -46,6 +46,7 @@ import resources.containers.DefaultPermissions; import resources.encodables.StringId; import resources.network.BaselineBuilder; import resources.network.DeltaBuilder; +import resources.objects.creature.CreatureObject; import resources.player.Player; import resources.player.PlayerState; import resources.server_info.Log; @@ -578,7 +579,7 @@ public abstract class SWGObject implements Serializable, Comparable { return (filledId != -1) ? filledId : -1; } - protected final void sendSceneCreateObject(Player target) { + private final void sendSceneCreateObject(Player target) { SceneCreateObjectByCrc create = new SceneCreateObjectByCrc(); create.setObjectId(objectId); create.setLocation(location); @@ -589,7 +590,7 @@ public abstract class SWGObject implements Serializable, Comparable { } - protected final void sendSceneDestroyObject(Player target) { + private final void sendSceneDestroyObject(Player target) { SceneDestroyObject destroy = new SceneDestroyObject(); destroy.setObjectId(objectId); target.sendPacket(destroy); @@ -607,6 +608,10 @@ public abstract class SWGObject implements Serializable, Comparable { target.sendPacket(new SceneEndBaselines(getObjectId())); } + public void destroyObject(Player target) { + sendSceneDestroyObject(target); + } + public void clearAware() { SWGObject [] objects; synchronized (objectsAware) { @@ -741,7 +746,7 @@ public abstract class SWGObject implements Serializable, Comparable { for (SWGObject swgObject : removed) { if (swgObject.getOwner() != null) { - sendSceneDestroyObject(swgObject.getOwner()); + destroyObject(swgObject.getOwner()); } } } @@ -766,7 +771,7 @@ public abstract class SWGObject implements Serializable, Comparable { if (objectsAware.remove(o)) { Player owner = o.getOwner(); if (owner != null) - sendSceneDestroyObject(owner); + destroyObject(owner); else destroyObjectObservers(o); } @@ -797,7 +802,7 @@ public abstract class SWGObject implements Serializable, Comparable { Set observers = new HashSet<>(); getChildrenObservers(observers, obj); for (SWGObject observer : observers) { - sendSceneDestroyObject(observer.getOwner()); + destroyObject(observer.getOwner()); } } @@ -854,6 +859,8 @@ public abstract class SWGObject implements Serializable, Comparable { // Now create the contained objects for (SWGObject containedObject : containedObjects.values()) { if (containedObject != null && !sentObjects.contains(containedObject)) { + if (containedObject instanceof CreatureObject && containedObject.hasSlot("ghost") && containedObject.getOwner() == null) + continue; // If it's a player, but that's logged out containedObject.createObject(target); } } diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 56498c884..a2f5eedb0 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -265,12 +265,12 @@ public class CreatureObject extends TangibleObject { public void setCashBalance(int cashBalance) { this.cashBalance = cashBalance; - sendDelta(1, 0, cashBalance); + sendDelta(1, 1, cashBalance); } public void setBankBalance(int bankBalance) { this.bankBalance = bankBalance; - sendDelta(1, 1, bankBalance); + sendDelta(1, 0, bankBalance); } public void setMovementScale(double movementScale) { @@ -682,8 +682,8 @@ public class CreatureObject extends TangibleObject { public void createBaseline1(Player target, BaselineBuilder bb) { super.createBaseline1(target, bb); // 0 variables - bb.addInt(cashBalance); // 0 - bb.addInt(bankBalance); // 1 + bb.addInt(bankBalance); // 0 + bb.addInt(cashBalance); // 1 bb.addObject(baseAttributes); // Attributes player has without any gear on -- 2 bb.addObject(skills); // 3 diff --git a/src/services/chat/ChatManager.java b/src/services/chat/ChatManager.java index 61a6224a3..672c2644d 100644 --- a/src/services/chat/ChatManager.java +++ b/src/services/chat/ChatManager.java @@ -65,12 +65,12 @@ import java.util.Locale; public class ChatManager extends Manager { - private ObjectDatabase mails; + private final ObjectDatabase mails; + private final ChatRoomService roomService; private int maxMailId; - private ChatRoomService roomService; - public ChatManager() { - roomService = new ChatRoomService(); + public ChatManager(Galaxy g) { + roomService = new ChatRoomService(g); mails = new CachedObjectDatabase<>("odb/mails.db"); maxMailId = 1; @@ -227,10 +227,6 @@ public class ChatManager extends Manager { } } - public void initializeGalaxyChannels(Galaxy galaxy) { - roomService.createSystemChannels(galaxy.getName()); - } - /* Friends List */ private void handleRequestFriendList(Player player) { diff --git a/src/services/chat/ChatRoomService.java b/src/services/chat/ChatRoomService.java index e5e97cfa8..ea296a911 100644 --- a/src/services/chat/ChatRoomService.java +++ b/src/services/chat/ChatRoomService.java @@ -32,12 +32,14 @@ import intents.chat.ChatRoomUpdateIntent; import intents.network.GalacticPacketIntent; import network.packets.Packet; import network.packets.swg.SWGPacket; +import network.packets.swg.zone.chat.ChatAddModeratorToRoom; import network.packets.swg.zone.chat.ChatBanAvatarFromRoom; import network.packets.swg.zone.chat.ChatCreateRoom; import network.packets.swg.zone.chat.ChatDestroyRoom; import network.packets.swg.zone.chat.ChatEnterRoomById; import network.packets.swg.zone.chat.ChatInviteAvatarToRoom; import network.packets.swg.zone.chat.ChatKickAvatarFromRoom; +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; @@ -46,17 +48,20 @@ 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; import network.packets.swg.zone.chat.ChatQueryRoomResults; import network.packets.swg.zone.chat.ChatRemoveAvatarFromRoom; +import network.packets.swg.zone.chat.ChatRemoveModeratorFromRoom; import network.packets.swg.zone.chat.ChatRequestRoomList; 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.Galaxy; import resources.Terrain; import resources.chat.ChatAvatar; import resources.chat.ChatResult; @@ -68,6 +73,8 @@ 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.ObjectDatabase; import services.player.PlayerManager; import java.util.ArrayList; @@ -81,12 +88,16 @@ import java.util.concurrent.ConcurrentHashMap; * @author Waverunner */ public class ChatRoomService extends Service { - private int maxChatRoomId; - private Map roomMap; // Map to keep track of each player's recent message for a room to prevent duplicates from client - private Map> messages; + private final Map> messages; + private final ObjectDatabase database; + private final Map roomMap; + private final Galaxy galaxy; + private int maxChatRoomId; - public ChatRoomService() { + public ChatRoomService(Galaxy g) { + galaxy = g; + database = new CachedObjectDatabase<>("odb/chat_rooms.db"); roomMap = new ConcurrentHashMap<>(); messages = new ConcurrentHashMap<>(); maxChatRoomId = 1; @@ -94,10 +105,17 @@ public class ChatRoomService extends Service { @Override public boolean initialize() { - // TODO: Load up persistent channels registerForIntent(ChatRoomUpdateIntent.TYPE); registerForIntent(GalacticPacketIntent.TYPE); + database.load(); + database.traverse((room) -> { + if (room.getId() >= maxChatRoomId) + maxChatRoomId++; + roomMap.put(room.getId(), room); + }); + + createSystemChannels(galaxy.getName()); return super.initialize(); } @@ -162,6 +180,13 @@ public class ChatRoomService extends Service { break; case CHAT_UNBAN_AVATAR_FROM_ROOM: if (p instanceof ChatUnbanAvatarFromRoom) handleChatUnbanAvatarFromRoom(player, (ChatUnbanAvatarFromRoom) p); + break; + case CHAT_ADD_MODERATOR_TO_ROOM: + if (p instanceof ChatAddModeratorToRoom) handleChatAddModeratorToRoom(player, (ChatAddModeratorToRoom) p); + break; + case CHAT_REMOVE_MODERATOR_FROM_ROOM: + if (p instanceof ChatRemoveModeratorFromRoom) handleChatRemoveModeratorFromRoom(player, (ChatRemoveModeratorFromRoom) p); + break; default: break; } } @@ -176,6 +201,79 @@ 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); + ChatAvatar sender = ChatAvatar.getFromPlayer(player); + + if (room == null) { + player.sendPacket(new ChatOnRemoveModeratorFromRoom(target, sender, ChatResult.ROOM_INVALID_NAME.getCode(), path, sequence)); + return; + } + + 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.getModerators().remove(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)); + } + + private void handleChatAddModeratorToRoom(Player player, ChatAddModeratorToRoom p) { + String path = p.getRoom(); + ChatAvatar target = p.getAvatar(); + int sequence = p.getSequence(); + + ChatRoom room = getRoom(path); + ChatAvatar sender = ChatAvatar.getFromPlayer(player); + + 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; + } + + // Remove from ban list + if (room.getBanned().remove(target)) { + room.sendPacketToMembers(player.getPlayerManager(), new ChatOnUnbanAvatarFromRoom(path, sender, target, ChatResult.SUCCESS.getCode(), 0)); + } + + if (!room.getModerators().add(target)) { + player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.NONE.getCode(), path, sequence)); + return; + } + + player.sendPacket(new ChatOnAddModeratorToRoom(target, sender, ChatResult.SUCCESS.getCode(), path, sequence)); + } + private void handleChatUnbanAvatarFromRoom(Player player, ChatUnbanAvatarFromRoom p) { String path = p.getRoom(); ChatAvatar target = p.getAvatar(); @@ -269,11 +367,6 @@ public class ChatRoomService extends Service { return; } - if (room.isMember(target)) { - player.sendPacket(new ChatOnKickAvatarFromRoom(target, sender, ChatResult.NONE.getCode(), path)); - return; - } - room.sendPacketToMembers(player.getPlayerManager(), new ChatOnKickAvatarFromRoom(target, sender, ChatResult.SUCCESS.getCode(), path)); } @@ -290,7 +383,7 @@ public class ChatRoomService extends Service { } if (room.isPublic()) { - player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.NONE.getCode(), p.getSequence())); + player.sendPacket(new ChatOnUninviteFromRoom(path, sender, invitee, ChatResult.CUSTOM_FAILURE.getCode(), p.getSequence())); return; } @@ -318,7 +411,7 @@ public class ChatRoomService extends Service { } if (room.isPublic()) { - player.sendPacket(new ChatOnInviteToRoom(path, sender, p.getAvatar(), ChatResult.NONE.getCode())); + player.sendPacket(new ChatOnInviteToRoom(path, sender, p.getAvatar(), ChatResult.CUSTOM_FAILURE.getCode())); return; } @@ -368,8 +461,8 @@ public class ChatRoomService extends Service { result = ChatResult.ROOM_ALREADY_EXISTS; if (result == ChatResult.SUCCESS) { - room = createRoom(ChatAvatar.getFromPlayer(player), p.isPublic(), path, title); - room.setMuted(p.isModerated()); + room = createRoom(ChatAvatar.getFromPlayer(player), p.isPublic(), path, title, true); + room.setModerated(p.isModerated()); } player.sendPacket(new ChatOnCreateRoom(result.getCode(), room, p.getSequence())); @@ -547,19 +640,24 @@ public class ChatRoomService extends Service { public void leaveChatChannel(Player player, String path) { for (ChatRoom chatRoom : roomMap.values()) { - if (chatRoom.getPath().equals(path)) + if (chatRoom.getPath().equals(path)) { leaveChatChannel(player, chatRoom, 0); + break; + } } } + /** - * Creates a new, non-persistent, chat room with the specified address path. + * 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) { + public ChatRoom createRoom(ChatAvatar creator, boolean isPublic, String path, String title, boolean persist) { if (path.isEmpty() || path.endsWith(".")) return null; @@ -567,12 +665,15 @@ public class ChatRoomService extends Service { 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, ""); + createRoom(creator, isPublic, parentPath, "", persist); } } @@ -588,9 +689,24 @@ public class ChatRoomService extends Service { room.setTitle(title); roomMap.put(id, room); + + if (persist) + database.put(id, 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) @@ -635,7 +751,7 @@ public class ChatRoomService extends Service { 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))); + rooms.handleRows((r) -> createRoom(systemAvatar, true, basePath + rooms.getCell(r, 0), (String) rooms.getCell(r, 1), true)); createPlanetChannels(systemAvatar, basePath); @@ -652,9 +768,9 @@ public class ChatRoomService extends Service { 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"); - createRoom(systemAvatar, true, path + "system", "system messages for this planet, cannot create rooms here"); - createRoom(systemAvatar, true, path + "Chat", "public chat for this planet, can create rooms here"); + 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); }); } diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java index 95937b0cf..e1f2daa28 100644 --- a/src/services/commands/CommandService.java +++ b/src/services/commands/CommandService.java @@ -172,6 +172,8 @@ public class CommandService extends Service { registerCallback("jumpServer", new JumpCmdCallback()); registerCallback("serverDestroyObject", new ServerDestroyObjectCmdCallback()); registerCallback("findFriend", new FindFriendCallback()); + registerCallback("setPlayerAppearance", new PlayerAppearanceCallback()); + registerCallback("revertPlayerAppearance", new RevertAppearanceCallback()); } private void clearCommands() { diff --git a/src/services/galaxy/GalacticManager.java b/src/services/galaxy/GalacticManager.java index cdc454208..894839b6f 100644 --- a/src/services/galaxy/GalacticManager.java +++ b/src/services/galaxy/GalacticManager.java @@ -53,7 +53,7 @@ public class GalacticManager extends Manager { objectManager = new ObjectManager(); playerManager = new PlayerManager(); gameManager = new GameManager(); - chatManager = new ChatManager(); + chatManager = new ChatManager(g); prevPacketIntent = null; addChildService(objectManager); @@ -65,7 +65,6 @@ public class GalacticManager extends Manager { @Override public boolean initialize() { registerForIntent(InboundPacketIntent.TYPE); - chatManager.initializeGalaxyChannels(galaxy); return super.initialize(); } diff --git a/src/services/network/NetworkClientManager.java b/src/services/network/NetworkClientManager.java index 548ef21d1..00b773962 100644 --- a/src/services/network/NetworkClientManager.java +++ b/src/services/network/NetworkClientManager.java @@ -35,9 +35,13 @@ import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import network.NetworkClient; import network.PacketReceiver; @@ -57,15 +61,30 @@ public class NetworkClientManager extends Manager implements PacketReceiver { private final Map > clients; private final Map networkClients; + private final Queue receivedPackets; + private final ExecutorService packetProcessor; private final Random crcGenerator; private final PacketSender packetSender; + private final Runnable processPacketRunnable; private long networkId; public NetworkClientManager(PacketSender packetSender) { this.packetSender = packetSender; clients = new HashMap>(); networkClients = new HashMap(); + receivedPackets = new LinkedList<>(); + packetProcessor = Executors.newCachedThreadPool(); crcGenerator = new Random(); + processPacketRunnable = new Runnable() { + public void run() { + synchronized (receivedPackets) { + ReceivedPacket recv = receivedPackets.poll(); + if (recv == null) + return; + handlePacket(recv.getType(), recv.getPacket()); + } + } + }; networkId = 0; } @@ -87,9 +106,12 @@ public class NetworkClientManager extends Manager implements PacketReceiver { @Override public void receivePacket(ServerType type, UDPPacket packet) { - handlePacket(type, packet); + synchronized (receivedPackets) { + receivedPackets.add(new ReceivedPacket(type, packet)); + } + packetProcessor.submit(processPacketRunnable); } - + @Override public void onIntentReceived(Intent i) { if (i instanceof OutboundPacketIntent) { @@ -276,4 +298,22 @@ public class NetworkClientManager extends Manager implements PacketReceiver { return false; } + private static class ReceivedPacket { + private final ServerType type; + private final UDPPacket packet; + + public ReceivedPacket(ServerType type, UDPPacket packet) { + this.type = type; + this.packet = packet; + } + + public ServerType getType() { + return type; + } + + public UDPPacket getPacket() { + return packet; + } + } + } diff --git a/src/services/objects/ObjectManager.java b/src/services/objects/ObjectManager.java index be4314258..94ff89a6d 100644 --- a/src/services/objects/ObjectManager.java +++ b/src/services/objects/ObjectManager.java @@ -205,7 +205,8 @@ public class ObjectManager extends Manager { private void loadObject(SWGObject obj) { obj.setOwner(null); - if (!(obj instanceof CreatureObject) || ((CreatureObject) obj).getPlayerObject() == null) + // if player is not a player + if (!(obj instanceof CreatureObject && ((CreatureObject) obj).hasSlot("ghost"))) objectAwareness.add(obj); if (obj instanceof CreatureObject && ((CreatureObject) obj).getPlayerObject() != null) { if (!obj.hasSlot("bank")) @@ -265,10 +266,14 @@ public class ObjectManager extends Manager { case PE_DISAPPEAR: p.getCreatureObject().clearAware(); objectAwareness.remove(p.getCreatureObject()); + for (SWGObject obj : p.getCreatureObject().getObservers()) + p.getCreatureObject().destroyObject(obj.getOwner()); break; case PE_ZONE_IN: p.getCreatureObject().clearAware(); objectAwareness.update(p.getCreatureObject()); + if (p.getCreatureObject().getParent() == null) + p.getCreatureObject().createObject(p); break; default: break; diff --git a/src/services/player/ZoneManager.java b/src/services/player/ZoneManager.java index d33b64eea..22e59a7ff 100644 --- a/src/services/player/ZoneManager.java +++ b/src/services/player/ZoneManager.java @@ -46,7 +46,6 @@ import network.packets.swg.zone.RequestGalaxyLoopTimes; import network.packets.swg.zone.SetWaypointColor; import network.packets.swg.zone.ShowBackpack; import network.packets.swg.zone.ShowHelmet; -import network.packets.swg.zone.UpdatePvpStatusMessage; import network.packets.swg.zone.chat.ChatOnConnectAvatar; import network.packets.swg.zone.chat.ChatSystemMessage; import network.packets.swg.zone.chat.VoiceChatStatus; @@ -136,8 +135,6 @@ public class ZoneManager extends Manager { sendZonePackets(player, creature); initPlayerBeforeZoneIn(player, creature, playerObj); - if (creature.getParent() == null) - creature.createObject(player); System.out.printf("[%s] %s is zoning in%n", player.getUsername(), player.getCharacterName()); Log.i("ObjectManager", "Zoning in %s with character %s", player.getUsername(), player.getCharacterName()); sendCommitHistory(player); @@ -189,7 +186,7 @@ public class ZoneManager extends Manager { sendPacket(player, new ParametersMessage()); sendPacket(player, new ChatOnConnectAvatar()); sendPacket(player, new CmdStartScene(false, objId, race, l, time)); - sendPacket(player, new UpdatePvpStatusMessage(creature.getPvpType(), creature.getPvpFactionId(), creature.getObjectId())); + flushPackets(); } private void initPlayerBeforeZoneIn(Player player, CreatureObject creatureObj, PlayerObject playerObj) { diff --git a/src/utilities/Encoder.java b/src/utilities/Encoder.java index d273624b1..d0c27aec2 100644 --- a/src/utilities/Encoder.java +++ b/src/utilities/Encoder.java @@ -40,7 +40,7 @@ public class Encoder { } public static byte[] encode(Object object, StringType strType) { - if (strType != StringType.UNSPECIFIED) { + if (strType != StringType.UNSPECIFIED && object instanceof String) { switch (strType) { case ASCII: return encodeAscii((String) object); case UNICODE: return encodeUnicode((String) object); diff --git a/src/utilities/IntentFactory.java b/src/utilities/IntentFactory.java new file mode 100644 index 000000000..f5ba6229d --- /dev/null +++ b/src/utilities/IntentFactory.java @@ -0,0 +1,42 @@ +package utilities; + +import resources.encodables.player.Mail; +import resources.objects.SWGObject; +import resources.player.Player; +import intents.chat.ChatBroadcastIntent; +import intents.chat.ChatBroadcastIntent.BroadcastType; +import intents.chat.PersistentMessageIntent; + +/** + * @author Mads + * This class is to be used exclusively in cases where using the intents directly isn't practical. + */ +public final class IntentFactory { + + private void broadcast(String message, Player source, BroadcastType type) { + new ChatBroadcastIntent(message, source, source.getCreatureObject().getTerrain(), type).broadcast(); + } + + public void broadcastArea(String message, Player source) { + broadcast(message, source, BroadcastType.AREA); + } + + public void broadcastGalaxy(String message, Player source) { + broadcast(message, source, BroadcastType.GALAXY); + } + + public void broadcastPersonal(String message, Player source) { + broadcast(message, source, BroadcastType.PERSONAL); + } + + public void broadcastPlanet(String message, Player source) { + broadcast(message, source, BroadcastType.PLANET); + } + + public void sendMail(SWGObject receiver, String sender, String subject, String message) { + Mail mail = new Mail(sender, subject, message, receiver.getObjectId()); + + new PersistentMessageIntent(receiver, mail, receiver.getOwner().getGalaxyName()).broadcast(); + } + +} diff --git a/src/utilities/Scripts.java b/src/utilities/Scripts.java index 409bd90e5..ff8859506 100644 --- a/src/utilities/Scripts.java +++ b/src/utilities/Scripts.java @@ -41,6 +41,10 @@ public class Scripts { private static final ScriptEngine ENGINE = new ScriptEngineManager().getEngineByName("nashorn"); private static final Invocable INVOCABLE = (Invocable) ENGINE; + static { + ENGINE.put("intentFactory", new IntentFactory()); + } + // Prevents instantiation. private Scripts() {}