diff --git a/scripts/commands/generic/cmdGroupDisband.js b/scripts/commands/generic/cmdGroupDisband.js
new file mode 100644
index 000000000..d52a8ccbf
--- /dev/null
+++ b/scripts/commands/generic/cmdGroupDisband.js
@@ -0,0 +1,14 @@
+function executeCommand(galacticManager, player, target, args) {
+ var GroupEventIntent = Java.type("intents.GroupEventIntent");
+ var GroupEventType = Java.type("intents.GroupEventIntent.GroupEventType");
+
+ if (target != null && args == null || args.length == 0) {
+ new GroupEventIntent(GroupEventType.GROUP_DISBAND, player, target).broadcast();
+ } else {
+ var kickingPlayer = galacticManager.getPlayerManager().getPlayerByCreatureFirstName(args);
+ if (kickingPlayer != null)
+ new GroupEventIntent(GroupEventType.GROUP_DISBAND, player, kickingPlayer.getCreatureObject()).broadcast();
+ else
+ new GroupEventIntent(GroupEventType.GROUP_DISBAND, player, null).broadcast();
+ }
+}
\ No newline at end of file
diff --git a/scripts/commands/generic/cmdGroupJoin.js b/scripts/commands/generic/cmdGroupJoin.js
new file mode 100644
index 000000000..1daf82dd8
--- /dev/null
+++ b/scripts/commands/generic/cmdGroupJoin.js
@@ -0,0 +1,6 @@
+function executeCommand(galacticManager, player, target, args) {
+ var GroupEventIntent = Java.type("intents.GroupEventIntent");
+ var GroupEventType = Java.type("intents.GroupEventIntent.GroupEventType");
+
+ new GroupEventIntent(GroupEventType.GROUP_JOIN, player).broadcast();
+}
\ No newline at end of file
diff --git a/scripts/commands/generic/groupInvite.js b/scripts/commands/generic/groupInvite.js
new file mode 100644
index 000000000..a91a82928
--- /dev/null
+++ b/scripts/commands/generic/groupInvite.js
@@ -0,0 +1,6 @@
+function executeCommand(galacticManager, player, target, args) {
+ var GroupEventIntent = Java.type("intents.GroupEventIntent");
+ var GroupEventType = Java.type("intents.GroupEventIntent.GroupEventType");
+
+ new GroupEventIntent(GroupEventType.GROUP_INVITE, player, target).broadcast();
+}
\ No newline at end of file
diff --git a/src/intents/GroupEventIntent.java b/src/intents/GroupEventIntent.java
new file mode 100644
index 000000000..7f00ac965
--- /dev/null
+++ b/src/intents/GroupEventIntent.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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 intents;
+
+import resources.control.Intent;
+import resources.objects.creature.CreatureObject;
+import resources.player.Player;
+
+/**
+ * Created by Waverunner on 10/4/2015
+ */
+public class GroupEventIntent extends Intent {
+ public static final String TYPE = "GroupEventIntent";
+
+ private GroupEventType eventType;
+ private Player player;
+ private CreatureObject target;
+
+ public GroupEventIntent(GroupEventType eventType, Player player, CreatureObject target) {
+ super(TYPE);
+ this.eventType = eventType;
+ this.player = player;
+ this.target = target;
+ }
+
+ public GroupEventIntent(GroupEventType eventType, Player player) {
+ super(TYPE);
+ this.eventType = eventType;
+ this.player = player;
+ }
+
+ public GroupEventType getEventType() {
+ return eventType;
+ }
+
+ public Player getPlayer() {
+ return player;
+ }
+
+ public CreatureObject getTarget() {
+ return target;
+ }
+
+ public enum GroupEventType {
+ GROUP_INVITE,
+ GROUP_JOIN,
+ GROUP_DISBAND
+ }
+}
\ No newline at end of file
diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java
index ce4b09440..0788d5ead 100644
--- a/src/resources/objects/SWGObject.java
+++ b/src/resources/objects/SWGObject.java
@@ -804,7 +804,7 @@ public abstract class SWGObject implements Serializable, Comparable {
}
}
- private void awarenessOutOfRange(SWGObject o) {
+ protected void awarenessOutOfRange(SWGObject o) {
synchronized (objectsAware) {
if (objectsAware.remove(o)) {
Player owner = o.getOwner();
@@ -816,7 +816,7 @@ public abstract class SWGObject implements Serializable, Comparable {
}
}
- private void awarenessInRange(SWGObject o) {
+ protected void awarenessInRange(SWGObject o) {
synchronized (objectsAware) {
if (objectsAware.add(o)) {
Player owner = o.getOwner();
@@ -827,7 +827,7 @@ public abstract class SWGObject implements Serializable, Comparable {
}
}
}
-
+
private void createObjectObservers(SWGObject obj) {
Set observers = new HashSet<>();
getChildrenObservers(observers, obj);
diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java
index 9f2c7034d..49ec60c33 100644
--- a/src/resources/objects/creature/CreatureObject.java
+++ b/src/resources/objects/creature/CreatureObject.java
@@ -45,6 +45,7 @@ import resources.objects.tangible.OptionFlag;
import resources.objects.tangible.TangibleObject;
import resources.objects.weapon.WeaponObject;
import resources.player.Player;
+import services.group.GroupInviterData;
import utilities.Encoder.StringType;
public class CreatureObject extends TangibleObject {
@@ -89,6 +90,7 @@ public class CreatureObject extends TangibleObject {
private boolean shownOnRadar = true;
private boolean beast = false;
private long groupId = 0;
+ private GroupInviterData inviterData = new GroupInviterData(0, null, "", 0);
private byte factionRank = 0;
private long ownerId = 0;
private int battleFatigue = 0;
@@ -521,6 +523,19 @@ public class CreatureObject extends TangibleObject {
return groupId;
}
+ public void updateGroupInviteData(Player sender, long groupId, String name) {
+ inviterData.setName(name);
+ inviterData.setSender(sender);
+ inviterData.setId(groupId);
+ inviterData.incrementCounter();
+
+ sendDelta(6, 14, inviterData);
+ }
+
+ public GroupInviterData getInviterData() {
+ return inviterData;
+ }
+
public void setGroupId(long groupId) {
this.groupId = groupId;
sendDelta(6, 13, groupId);
@@ -807,9 +822,7 @@ public class CreatureObject extends TangibleObject {
bb.addAscii(moodAnimation); // 11
bb.addLong(equippedWeaponId); // 12
bb.addLong(groupId); // 13
- bb.addLong(0); // Group Inviter ID -- 14
- bb.addAscii(""); // Group Inviter Name
- bb.addLong(0); // Invite counter
+ bb.addObject(inviterData); // TODO: Check structure -- 14
bb.addInt(guildId); // 15
bb.addLong(lookAtTargetId); // 16
bb.addLong(intendedTargetId); // 17
diff --git a/src/resources/objects/group/GroupObject.java b/src/resources/objects/group/GroupObject.java
index e251cb8eb..d72eb1b2c 100644
--- a/src/resources/objects/group/GroupObject.java
+++ b/src/resources/objects/group/GroupObject.java
@@ -27,32 +27,213 @@
package resources.objects.group;
+import intents.NotifyPlayersPacketIntent;
+import network.packets.Packet;
+import network.packets.swg.zone.baselines.Baseline;
+import resources.collections.SWGList;
+import resources.encodables.Encodable;
import resources.network.BaselineBuilder;
import resources.objects.SWGObject;
+import resources.objects.creature.CreatureObject;
import resources.player.Player;
+import utilities.Encoder;
-public class GroupObject extends SWGObject {
-
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class GroupObject extends SWGObject { // Extends INTO or TANO?
private static final long serialVersionUID = 200L;
-
+
+ private final SWGList groupMembers = new SWGList<>(Baseline.BaselineType.GRUP, 6, 2, Encoder.StringType.ASCII);
+ private long leader;
+ private short level;
+ private long lootMaster;
+ private int lootRule;
+
+ private transient PickupPointTimer pickupPointTimer;
+
+ public GroupObject(long objectId) {
+ super(objectId, Baseline.BaselineType.GRUP);
+ }
+
+ @Override
+ public void createBaseline3(Player target, BaselineBuilder bb) {
+ super.createBaseline3(target, bb);
+ }
+
@Override
public void createBaseline6(Player target, BaselineBuilder bb) {
super.createBaseline6(target, bb); // BASE06 -- 2 variables
- bb.addInt(0); // groupMembers // 2
- bb.addInt(0); // updateCount
+ bb.addObject(groupMembers); // 2 -- NOTE: First person is the leader
bb.addInt(0); // formationmembers // 3
bb.addInt(0); // updateCount
bb.addAscii(""); // groupName // 4
- bb.addShort(0); // groupLevel // 5
+ bb.addShort(level); // 5
bb.addInt(0); // formationNameCrc // 6
- bb.addLong(0); // lootMaster // 7
- bb.addInt(0); // lootRule // 8
- bb.addInt(0); // PickupPointTimer startTime // 9
- bb.addInt(0); // endTime
+ bb.addLong(lootMaster); // 7
+ bb.addInt(lootRule); // 8
+ bb.addObject(pickupPointTimer); // 9
bb.addAscii(""); // PickupPoint planetName // 10
bb.addFloat(0); // x
bb.addFloat(0); // y
bb.addFloat(0); // z
+
bb.incrementOperandCount(9);
}
+
+ public void addMember(CreatureObject object) {
+ groupMembers.add(new GroupMember(object.getObjectId(), object.getName()));
+
+ groupMembers.sendDeltaMessage(this);
+
+ awarenessInRange(object);
+ object.setGroupId(getObjectId());
+
+ if (object.getLevel() > level)
+ setLevel((short) object.getLevel());
+ }
+
+ public void removeMember(CreatureObject object) {
+ synchronized (groupMembers) {
+ groupMembers.remove(new GroupMember(object.getObjectId(), object.getName()));
+
+ object.setGroupId(0);
+ awarenessOutOfRange(object);
+
+ groupMembers.sendDeltaMessage(this);
+ }
+ }
+
+ public long getLeader() {
+ return leader;
+ }
+
+ public void setLeader(CreatureObject object) {
+ this.leader = object.getObjectId();
+
+ GroupMember member = new GroupMember(object.getObjectId(), object.getName());
+ if (groupMembers.size() > 0) {
+ synchronized (groupMembers) {
+ GroupMember previous = groupMembers.set(0, member);
+ groupMembers.add(previous);
+ }
+ } else {
+ groupMembers.add(member);
+ }
+ groupMembers.sendDeltaMessage(this);
+ }
+
+ public short getLevel() {
+ return level;
+ }
+
+ public void setLevel(short level) {
+ this.level = level;
+ }
+
+ public long getLootMaster() {
+ return lootMaster;
+ }
+
+ public void setLootMaster(long lootMaster) {
+ this.lootMaster = lootMaster;
+ }
+
+ public int getLootRule() {
+ return lootRule;
+ }
+
+ public void setLootRule(int lootRule) {
+ this.lootRule = lootRule;
+ sendDelta(6, 8, lootRule);
+ }
+
+ public Map getGroupMembers() {
+ Map members = new HashMap<>();
+
+ synchronized (groupMembers) {
+ for (GroupMember groupMember : groupMembers) {
+ members.put(groupMember.getName(), groupMember.getId());
+ }
+ }
+
+ return members;
+ }
+
+ private static class PickupPointTimer implements Serializable, Encodable {
+ public int start;
+ public int end;
+
+ @Override
+ public byte[] encode() {
+ return ByteBuffer.allocate(8).putInt(start).putInt(end).array();
+ }
+
+ @Override
+ public void decode(ByteBuffer data) {
+ start = Packet.getInt(data);
+ end = Packet.getInt(data);
+ }
+
+ public int decrement() {
+ return --end;
+ }
+ }
+
+ private static class GroupMember implements Serializable, Encodable {
+ private long id;
+ private String name;
+
+ public GroupMember(long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public byte[] encode() {
+ ByteBuffer bb = ByteBuffer.allocate(10 + name.length());
+ Packet.addLong(bb, id);
+ Packet.addAscii(bb, name);
+ return bb.array();
+ }
+
+ @Override
+ public void decode(ByteBuffer data) {
+ id = Packet.getLong(data);
+ name = Packet.getAscii(data);
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ GroupMember that = (GroupMember) o;
+
+ return id == that.id && name.equals(that.name);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (id ^ (id >>> 32));
+ result = 31 * result + name.hashCode();
+ return result;
+ }
+ }
}
diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java
index afee2de04..569b197a3 100644
--- a/src/services/commands/CommandService.java
+++ b/src/services/commands/CommandService.java
@@ -100,6 +100,7 @@ public class CommandService extends Service {
}
Command command = getCommand(request.getCommandCrc());
+ System.out.println(command + "\t\t\tscriptHook: " + command.getScriptHook() + "\t\t:cppHook " + command.getDefaultScriptCallback());
String [] arguments = request.getArguments().split(" ");
SWGObject target = null;
if (request.getTargetId() != 0) {
@@ -151,7 +152,8 @@ public class CommandService extends Service {
}
private void loadBaseCommands() {
- final String [] commandTables = new String [] {"command_table", "client_command_table", "command_table_ground"};
+ // First = Higher Priority, Last = Lower Priority ---- Some tables contain duplicates, ORDER MATTERS!
+ final String [] commandTables = new String [] {"command_table", "command_table_ground", "client_command_table", };
clearCommands();
for (String table : commandTables) {
@@ -259,7 +261,10 @@ public class CommandService extends Service {
}
}
- private void addCommand(Command command) {
+ private boolean addCommand(Command command) {
+ if (commands.containsKey(command.getCrc()))
+ return false;
+
synchronized (commands) {
commands.put(command.getCrc(), command);
}
@@ -276,6 +281,6 @@ public class CommandService extends Service {
}
commands.add(command);
}
+ return true;
}
-
}
diff --git a/src/services/galaxy/GameManager.java b/src/services/galaxy/GameManager.java
index a0b7747da..5aed2ab25 100644
--- a/src/services/galaxy/GameManager.java
+++ b/src/services/galaxy/GameManager.java
@@ -31,6 +31,7 @@ import resources.control.Manager;
import services.commands.CommandService;
import services.faction.FactionService;
import services.galaxy.terminals.TerminalService;
+import services.group.GroupService;
import services.sui.SuiService;
public class GameManager extends Manager {
@@ -41,7 +42,8 @@ public class GameManager extends Manager {
private final WeatherService weatherService;
private final TerminalService terminalManager;
private final FactionService factionService;
-
+ private final GroupService groupService;
+
public GameManager() {
commandService = new CommandService();
connectionService = new ConnectionService();
@@ -49,6 +51,7 @@ public class GameManager extends Manager {
weatherService = new WeatherService();
terminalManager = new TerminalService();
factionService = new FactionService();
+ groupService = new GroupService();
addChildService(commandService);
addChildService(connectionService);
@@ -56,5 +59,6 @@ public class GameManager extends Manager {
addChildService(weatherService);
addChildService(terminalManager);
addChildService(factionService);
+ addChildService(groupService);
}
}
diff --git a/src/services/group/GroupInviterData.java b/src/services/group/GroupInviterData.java
new file mode 100644
index 000000000..5c0c0136b
--- /dev/null
+++ b/src/services/group/GroupInviterData.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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 services.group;
+
+import network.packets.Packet;
+import resources.encodables.Encodable;
+import resources.player.Player;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+
+/**
+ * Created by Waverunner on 10/4/2015
+ */
+public class GroupInviterData implements Encodable, Serializable {
+
+ private long id;
+ private Player sender;
+ private String name;
+ private long counter;
+
+ public GroupInviterData(long id, Player sender, String name, int counter) {
+ this.id = id;
+ this.sender = sender;
+ this.name = name;
+ this.counter = counter;
+ }
+
+ @Override
+ public byte[] encode() {
+ ByteBuffer bb = ByteBuffer.allocate(name.length() + 18);
+
+ Packet.addLong(bb, id);
+ Packet.addAscii(bb, name);
+ Packet.addLong(bb, counter);
+
+ return bb.array();
+ }
+
+ @Override
+ public void decode(ByteBuffer data) {
+ id = Packet.getLong(data);
+ name = Packet.getAscii(data);
+ counter = Packet.getInt(data);
+ }
+
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getCounter() {
+ return counter;
+ }
+
+ public void incrementCounter() {
+ counter++;
+ }
+
+ public Player getSender() {
+ return sender;
+ }
+
+ public void setSender(Player sender) {
+ this.sender = sender;
+ }
+}
diff --git a/src/services/group/GroupService.java b/src/services/group/GroupService.java
new file mode 100644
index 000000000..0b2fddf4f
--- /dev/null
+++ b/src/services/group/GroupService.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * 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 services.group;
+
+import intents.GroupEventIntent;
+import intents.NotifyPlayersPacketIntent;
+import intents.PlayerEventIntent;
+import intents.chat.ChatRoomUpdateIntent;
+import intents.object.ObjectCreateIntent;
+import intents.object.ObjectIdRequestIntent;
+import intents.object.ObjectIdResponseIntent;
+import network.packets.swg.zone.chat.ChatSystemMessage;
+import resources.chat.ChatAvatar;
+import resources.control.Intent;
+import resources.control.Service;
+import resources.encodables.OutOfBandPackage;
+import resources.encodables.ProsePackage;
+import resources.encodables.StringId;
+import resources.objects.creature.CreatureObject;
+import resources.objects.group.GroupObject;
+import resources.player.Player;
+import resources.server_info.Log;
+import services.objects.ObjectCreator;
+import services.player.PlayerManager;
+import utilities.IntentFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by Waverunner on 10/4/2015
+ */
+public class GroupService extends Service {
+
+ private final List reservedIds = new ArrayList<>();
+ private final Map groups = new HashMap<>();
+
+ @Override
+ public boolean initialize() {
+ registerForIntent(GroupEventIntent.TYPE);
+ registerForIntent(PlayerEventIntent.TYPE);
+ registerForIntent(ObjectIdResponseIntent.TYPE);
+ return super.initialize();
+ }
+
+ @Override
+ public boolean start() {
+ new ObjectIdRequestIntent("GroupService", 50).broadcast();
+ return super.start();
+ }
+
+ @Override
+ public void onIntentReceived(Intent i) {
+ String type = i.getType();
+ switch(type) {
+ case GroupEventIntent.TYPE:
+ if (i instanceof GroupEventIntent)
+ handleGroupEventIntent((GroupEventIntent) i);
+ break;
+ case ObjectIdResponseIntent.TYPE:
+ if (!(i instanceof ObjectIdResponseIntent) || !((ObjectIdResponseIntent) i).getIdentifier().equals("GroupService"))
+ break;
+ reservedIds.addAll(((ObjectIdResponseIntent)i).getReservedIds());
+ break;
+ case PlayerEventIntent.TYPE:
+ break;
+ default: break;
+ }
+ }
+
+ private void handleGroupEventIntent(GroupEventIntent intent) {
+ switch(intent.getEventType()) {
+ case GROUP_INVITE:
+ handleGroupInvite(intent.getPlayer(), intent.getTarget());
+ break;
+ case GROUP_JOIN:
+ handleGroupJoin(intent.getPlayer());
+ break;
+ case GROUP_DISBAND:
+ handleGroupDisband(intent.getPlayer(), intent.getTarget());
+ break;
+ }
+ }
+
+ private void handleGroupDisband(Player player, CreatureObject target) {
+ CreatureObject creo = player.getCreatureObject();
+
+ if (creo == null)
+ return;
+
+ GroupObject group = getGroup(creo.getGroupId());
+ if (group == null)
+ return;
+
+ if (group.getLeader() != creo.getObjectId()) {
+ sendSystemMessage(player, "must_be_leader");
+ return;
+ }
+
+ if (target == null) {
+ // Disband Group
+
+ String galaxy = player.getGalaxyName();
+ new ChatRoomUpdateIntent(getGroupChatPath(group.getObjectId(), galaxy), String.valueOf(group.getObjectId()), null,
+ ChatAvatar.getSystemAvatar(galaxy), null, ChatRoomUpdateIntent.UpdateType.DESTROY).broadcast();
+
+ Map members = group.getGroupMembers();
+ PlayerManager playerManager = player.getPlayerManager();
+
+ sendGroupSystemMessage(group, "disbanded");
+ for (String name : members.keySet()) {
+ Player memPlayer = playerManager.getPlayerByCreatureName(name);
+ if (memPlayer == null)
+ continue;
+
+ CreatureObject memCreo = memPlayer.getCreatureObject();
+ if (memCreo == null)
+ continue;
+
+ group.removeMember(memCreo);
+ }
+
+ // TODO: Send object destroy intent for object manager
+ } else {
+ // Disband player
+
+ group.removeMember(target);
+
+ sendGroupSystemMessage(group, "other_left_prose", "TU", target.getObjectId());
+ sendSystemMessage(target.getOwner(), "removed");
+
+ // TODO: Leave group chat room
+ }
+ }
+
+ private void handleGroupInvite(Player player, CreatureObject target) {
+ CreatureObject playerCreo = player.getCreatureObject();
+
+ Player targetOwner = target.getOwner();
+ if (targetOwner == null)
+ return;
+
+ long groupId = playerCreo.getGroupId();
+ long inviterId = playerCreo.getObjectId();
+ long targetId = target.getObjectId();
+
+ if (groupId != 0) {
+ GroupObject group = getGroup(groupId);
+
+ if (group.getLeader() != inviterId) {
+ sendSystemMessage(player, "must_be_leader");
+ return;
+ }
+
+ if (target.getGroupId() != 0) {
+ sendSystemMessage(player, "already_grouped", "TT", targetId);
+ return;
+ }
+
+ if (target.getInviterData().getId() != 0 ) {
+ if(target.getInviterData().getId() != groupId)
+ sendSystemMessage(player, "considering_other_group");
+ else
+ sendSystemMessage(player, "considering_your_group");
+ }
+ } else {
+ sendSystemMessage(targetOwner, "invite_target", "TT", inviterId);
+ sendSystemMessage(player, "invite_leader", "TT", targetId);
+
+ target.updateGroupInviteData(player, -1, player.getCharacterName());
+ }
+ }
+
+ private void handleGroupJoin(Player player) {
+ CreatureObject creo = player.getCreatureObject();
+
+ GroupInviterData invitation = creo.getInviterData();
+
+ long groupId = invitation.getId();
+
+ if (groupId == 0) {
+ sendSystemMessage(player, "must_be_invited");
+ return;
+ }
+
+ GroupObject group = getGroup(groupId);
+
+ Player sender = invitation.getSender();
+
+ if (invitation.getSender() == null) {
+ // Inviter logged out, invitation is no good
+ sendSystemMessage(player, "must_be_invited");
+ creo.updateGroupInviteData(null, 0, "");
+ return;
+ }
+
+ // Group doesn't exist yet
+ if (group == null) {
+
+ group = createGroup(sender);
+
+ if (group == null) {
+ Log.e("GroupService", "Failed to create group from sender %s for %s", sender, player);
+ creo.updateGroupInviteData(null, 0, "");
+ return;
+ }
+
+ group.addMember(sender.getCreatureObject());
+
+ sendSystemMessage(sender, "formed_self", "TT", sender.getCreatureObject().getObjectId());
+
+ // TODO: Join group chat room
+ } else {
+ // Group already exists
+
+ if (group.getLeader() != sender.getCreatureObject().getObjectId()) {
+ sendSystemMessage(player, "join_inviter_not_leader", sender.getCreatureObject().getObjectId());
+ creo.updateGroupInviteData(null, 0, "");
+ return;
+ }
+
+ if (group.getGroupMembers().size() == 8) {
+ sendSystemMessage(player, "join_full");
+ creo.updateGroupInviteData(null, 0, "");
+ return;
+ }
+ }
+ sendGroupSystemMessage(group, "other_joined_prose", "TU", creo.getObjectId());
+ group.addMember(creo);
+ sendSystemMessage(player, "joined_self");
+ creo.updateGroupInviteData(null, 0, "");
+ // TODO: Join group chat room
+ }
+
+ private GroupObject createGroup(Player player) {
+ GroupObject group = (GroupObject) ObjectCreator.createObjectFromTemplate(getNextObjectId(), "object/group/shared_group_object.iff");
+ if (group == null)
+ return null;
+
+ group.setLeader(player.getCreatureObject());
+
+ groups.put(group.getObjectId(), group);
+
+ new ObjectCreateIntent(group, false).broadcast();
+
+ String galaxy = player.getGalaxyName();
+ new ChatRoomUpdateIntent(getGroupChatPath(group.getObjectId(), galaxy), String.valueOf(group.getObjectId()), null,
+ ChatAvatar.getSystemAvatar(galaxy), null, ChatRoomUpdateIntent.UpdateType.CREATE).broadcast();
+
+ return group;
+ }
+
+ private long getNextObjectId() {
+ synchronized (reservedIds) {
+ if (reservedIds.size() <= 5)
+ new ObjectIdRequestIntent("GroupService", 50).broadcast();
+
+ return reservedIds.remove(0);
+ }
+ }
+
+ private void sendGroupSystemMessage(GroupObject group, String id) {
+ Map members = group.getGroupMembers();
+
+ List ids = new ArrayList<>(members.values());
+
+ new NotifyPlayersPacketIntent(new ChatSystemMessage(ChatSystemMessage.SystemChatType.SCREEN_AND_CHAT, "@group:" + id), ids).broadcast();
+ }
+
+ private void sendGroupSystemMessage(GroupObject group, String id, Object ... objects) {
+ Map members = group.getGroupMembers();
+
+ List ids = new ArrayList<>(members.values());
+
+ if (objects.length % 2 != 0)
+ Log.e("ProsePackage", "Sent a ProsePackage chat message with an uneven number of object arguments for StringId %s", "@group:" + id);
+ Object [] prose = new Object[objects.length + 2];
+ prose[0] = "StringId";
+ prose[1] = new StringId("@group:" + id);
+ System.arraycopy(objects, 0, prose, 2, objects.length);
+
+ new NotifyPlayersPacketIntent(
+ new ChatSystemMessage(ChatSystemMessage.SystemChatType.SCREEN_AND_CHAT,
+ new OutOfBandPackage(new ProsePackage(prose))), ids).broadcast();
+ }
+
+ private String getGroupChatPath(long groupId, String galaxy) {
+ // SWG.serverName.group.GroupObjectId.GroupChat || title = groupid
+ String groupIdString = String.valueOf(groupId);
+ return "SWG." + galaxy + ".group." + groupIdString + ".GroupChat";
+ }
+ private GroupObject getGroup(long groupId) {
+ return groups.get(groupId);
+ }
+
+ private void sendSystemMessage(Player target, String id) {
+ target.sendPacket(new ChatSystemMessage(ChatSystemMessage.SystemChatType.SCREEN_AND_CHAT, id));
+ }
+
+ private void sendSystemMessage(Player target, String id, Object ... objects) {
+ IntentFactory.sendSystemMessage(target, "@group:" + id, objects);
+ }
+}
diff --git a/src/services/objects/ObjectCreator.java b/src/services/objects/ObjectCreator.java
index 99561fc80..1e4681ae7 100644
--- a/src/services/objects/ObjectCreator.java
+++ b/src/services/objects/ObjectCreator.java
@@ -37,6 +37,7 @@ import resources.objects.SWGObject;
import resources.objects.building.BuildingObject;
import resources.objects.cell.CellObject;
import resources.objects.creature.CreatureObject;
+import resources.objects.group.GroupObject;
import resources.objects.installation.InstallationObject;
import resources.objects.intangible.IntangibleObject;
import resources.objects.player.PlayerObject;
@@ -80,6 +81,7 @@ public final class ObjectCreator {
case "installation": return new InstallationObject(objectId);
case "ship": return new ShipObject(objectId);
case "soundobject": return new SoundObject(objectId);
+ case "group": return new GroupObject(objectId);
}
return null;
}
diff --git a/src/utilities/IntentFactory.java b/src/utilities/IntentFactory.java
index 07fca1c1b..59bc2e495 100644
--- a/src/utilities/IntentFactory.java
+++ b/src/utilities/IntentFactory.java
@@ -13,6 +13,7 @@ import resources.player.Player;
import intents.chat.ChatBroadcastIntent;
import intents.chat.ChatBroadcastIntent.BroadcastType;
import intents.chat.PersistentMessageIntent;
+import resources.server_info.Log;
import resources.sui.SuiWindow;
/**
@@ -76,7 +77,9 @@ public final class IntentFactory {
* set the %DI to the value of 500 for the StringId.
* Note that the prose key must always come first and the value for that key must always come second.
*/
- public void sendSystemMessage(Player target, String stf, Object ... objects) {
+ public static void sendSystemMessage(Player target, String stf, Object ... objects) {
+ if (objects.length % 2 != 0)
+ Log.e("ProsePackage", "Sent a ProsePackage chat message with an uneven number of object arguments for StringId %s", stf);
Object [] prose = new Object[objects.length + 2];
prose[0] = "StringId";
prose[1] = new StringId(stf);