Additional syntax based refactoring to CommandService

This commit is contained in:
Obique PSWG
2017-04-11 08:16:02 -05:00
parent 499eaf16f0
commit db652d6ce8
2 changed files with 55 additions and 255 deletions
+5 -1
View File
@@ -102,10 +102,14 @@ public class CommandLauncher {
// TODO implement locomotion and state checks up here. See action and error in CommandQueueDequeue!
// TODO target and targetType checks
Log.d("doCommand %s", command.getName());
sendCommandDequeue(player, command, enqueued.getRequest(), 0, 0);
if (!cooldownHandler.startCooldowns(creature, enqueued))
if (!cooldownHandler.startCooldowns(creature, enqueued)) {
Log.w("Not starting command %s - cooldown not ready yet!", command.getName());
return; // This ability is currently on cooldown
}
executeCommand(player, enqueued);
new ChatCommandIntent(creature, enqueued.getTarget(), command, enqueued.getRequest().getArguments().split(" ")).broadcast();
+50 -254
View File
@@ -28,15 +28,10 @@
package services.commands;
import intents.PlayerEventIntent;
import intents.chat.ChatBroadcastIntent;
import intents.chat.ChatCommandIntent;
import intents.network.GalacticPacketIntent;
import intents.player.PlayerTransformedIntent;
import java.io.FileNotFoundException;
import network.packets.Packet;
import network.packets.swg.zone.object_controller.CommandQueueDequeue;
import network.packets.swg.zone.object_controller.CommandQueueEnqueue;
import resources.client_info.ClientFactory;
import resources.client_info.visitors.DatatableData;
@@ -48,30 +43,17 @@ import resources.commands.Command;
import resources.commands.ICmdCallback;
import resources.commands.callbacks.*;
import resources.common.CRC;
import resources.encodables.ProsePackage;
import resources.encodables.StringId;
import resources.objects.SWGObject;
import resources.objects.weapon.WeaponType;
import resources.player.AccessLevel;
import resources.player.Player;
import resources.server_info.Log;
import utilities.Scripts;
import services.commands.CommandLauncher.EnqueuedCommand;
import services.galaxy.GalacticManager;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import com.projectswg.common.concurrency.PswgScheduledThreadPool;
import com.projectswg.common.concurrency.SynchronizedMap;
import com.projectswg.common.control.Service;
import network.packets.swg.zone.object_controller.CommandTimer;
import resources.combat.DelayAttackEggPosition;
import resources.combat.HitType;
import resources.commands.DefaultPriority;
@@ -79,16 +61,12 @@ import resources.objects.creature.CreatureObject;
public class CommandService extends Service {
private final Map <Player, BlockingQueue<QueuedCommand>>combatQueueMap;
private final Map <CreatureObject, Set<String>> cooldownMap;
private final PswgScheduledThreadPool cooldownThread;
private final CommandContainer commandContainer;
private final CommandContainer commandContainer;
private final CommandLauncher commandLauncher;
public CommandService() {
this.combatQueueMap = new SynchronizedMap<>();
this.cooldownMap = new HashMap<>();
this.cooldownThread = new PswgScheduledThreadPool(1, "command-service-cooldown-thread");
this.commandContainer = new CommandContainer();
this.commandLauncher = new CommandLauncher();
registerForIntent(GalacticPacketIntent.class, gpi -> handleGalacticPacketIntent(gpi));
registerForIntent(PlayerEventIntent.class, pei -> handlePlayerEventIntent(pei));
@@ -100,15 +78,13 @@ public class CommandService extends Service {
loadBaseCommands();
loadCombatCommands();
registerCallbacks();
cooldownThread.start();
cooldownThread.executeWithFixedRate(1000, 1000, () -> pollQueues());
commandLauncher.start();
return super.initialize();
}
@Override
public boolean terminate() {
cooldownThread.stop();
commandLauncher.stop();
return super.terminate();
}
@@ -121,11 +97,11 @@ public class CommandService extends Service {
}
private void handlePlayerEventIntent(PlayerEventIntent pei) {
switch(pei.getEvent()) {
switch (pei.getEvent()) {
case PE_LOGGED_OUT:
// No reason to keep their combat queue in the map if they log out
// This also prevents queued commands from executing after the player logs out
combatQueueMap.remove(pei.getPlayer());
commandLauncher.removePlayerFromQueue(pei.getPlayer());
break;
default:
break;
@@ -134,18 +110,13 @@ public class CommandService extends Service {
private void handlePlayerTransformedIntent(PlayerTransformedIntent pti) {
CreatureObject creature = pti.getPlayer();
if (creature.isPerforming()) {
// A performer can transform while dancing...
return;
}
Player player = creature.getOwner();
Queue<QueuedCommand> combatQueue = combatQueueMap.get(player);
if (combatQueue != null) {
combatQueue.clear();
}
commandLauncher.removePlayerFromQueue(creature.getOwner());
}
private void handleCommandRequest(Player player, GalacticManager galacticManager, CommandQueueEnqueue request) {
@@ -158,160 +129,22 @@ public class CommandService extends Service {
Command command = commandContainer.getCommand(request.getCommandCrc());
// TODO target and target type checks below. Work with Set<TangibleObject> targets from there
long targetId = request.getTargetId();
final SWGObject target = targetId != 0 ? galacticManager.getObjectManager().getObjectById(targetId) : null;
SWGObject target = targetId != 0 ? galacticManager.getObjectManager().getObjectById(targetId) : null;
EnqueuedCommand enqueued = new EnqueuedCommand(command, galacticManager, target, request);
if (!command.getCooldownGroup().equals("defaultCooldownGroup") && command.isAddToCombatQueue()) {
// Schedule for later execution
BlockingQueue<QueuedCommand> combatQueue = combatQueueMap.get(player);
if (combatQueue == null) {
combatQueue = new PriorityBlockingQueue<>(); // Has natural ordering. QueuedCommand implements Comparable for this purpose.
combatQueueMap.put(player, combatQueue);
}
if (!combatQueue.offer(new QueuedCommand(command, galacticManager, target, request))) {
// Ziggy: Shouldn't happen, unless the Queue implementation is changed
Log.e("Unable to enqueue command %s from %s because the combat queue is full", command.getName(), player.getCreatureObject());
}
commandLauncher.addToQueue(player, enqueued);
} else {
// Execute it now
doCommand(galacticManager, player, command, target, request);
commandLauncher.doCommand(player, enqueued);
}
}
private void pollQueues() {
// Takes the head of each queue and executes the command
combatQueueMap.forEach((player, combatQueue) -> {
QueuedCommand queueHead = combatQueue.poll();
if (queueHead != null) { // Can be null if the combat queue is empty
doCommand(queueHead.getGalacticManager(), player, queueHead.getCommand(), queueHead.getTarget(), queueHead.getRequest());
}
});
}
private void doCommand(GalacticManager galacticManager, Player player, Command command, SWGObject target, CommandQueueEnqueue request) {
CreatureObject creature = player.getCreatureObject();
// TODO implement locomotion and state checks up here. See action and error in CommandQueueDequeue!
// TODO target and targetType checks
// Let's check if this ability is on cooldown
String cooldownGroup = command.getCooldownGroup();
String cooldownGroup2 = command.getCooldownGroup2();
synchronized (cooldownMap) {
Set<String> cooldowns = cooldownMap.get(creature);
if (cooldowns == null) {
// This is the first time they're using a cooldown command
cooldowns = new HashSet<>();
cooldownMap.put(creature, cooldowns);
} else if (cooldowns.contains(cooldownGroup) || cooldowns.contains(cooldownGroup2)) {
// This ability is currently on cooldown
sendCommandDequeue(player, command, request, 0, 0);
return;
}
}
sendCommandDequeue(player, command, request, 0, 0);
String argumentString = request.getArguments();
String[] arguments = argumentString.split(" ");
executeCommand(galacticManager, player, command, target, argumentString);
new ChatCommandIntent(creature, target, command, arguments).broadcast();
// TODO custom cooldown times. Scripts might be a good idea.
startCooldownGroup(creature, request.getCounter(), command.getCrc(), cooldownGroup, command.getCooldownTime());
startCooldownGroup(creature, request.getCounter(), command.getCrc(), cooldownGroup2, command.getCooldownTime2());
}
private void sendCommandDequeue(Player player, Command command, CommandQueueEnqueue request, int action, int error) {
CommandQueueDequeue dequeue = new CommandQueueDequeue(player.getCreatureObject().getObjectId());
dequeue.setCounter(request.getCounter());
dequeue.setAction(action);
dequeue.setError(error);
dequeue.setTimer(command.getExecuteTime());
player.sendPacket(dequeue);
}
private void startCooldownGroup(CreatureObject creature, int sequenceId, int commandNameCrc, String cooldownGroup, float cooldownTime) {
if(!cooldownGroup.isEmpty() && !cooldownGroup.equals("defaultCooldownGroup")) {
synchronized(cooldownMap) {
if(cooldownMap.get(creature).add(cooldownGroup)) {
CommandTimer commandTimer = new CommandTimer(creature.getObjectId());
commandTimer.setCooldownGroupCrc(CRC.getCrc(cooldownGroup));
commandTimer.setCooldownMax(cooldownTime);
commandTimer.setCommandNameCrc(commandNameCrc);
commandTimer.setSequenceId(sequenceId);
creature.sendSelf(commandTimer);
cooldownThread.execute((long) (cooldownTime * 1000), () -> removeCooldown(creature, cooldownGroup));
}
}
}
}
private void removeCooldown(CreatureObject creature, String cooldownGroup) {
synchronized (cooldownMap) {
Set<String> cooldownGroups = cooldownMap.get(creature);
if (cooldownGroups.remove(cooldownGroup)) {
} else {
Log.w("%s doesn't have cooldown group %s!", creature, cooldownGroup);
}
}
}
private void executeCommand(GalacticManager galacticManager, Player player, Command command, SWGObject target, String args) {
if (player.getCreatureObject() == null) {
Log.e("No creature object associated with the player '%s'!", player.getUsername());
return;
}
if(player.getAccessLevel().getValue() < command.getGodLevel()) {
String commandAccessLevel = AccessLevel.getFromValue(command.getGodLevel()).toString();
String playerAccessLevel = player.getAccessLevel().toString();
Log.i("[%s] attempted to use the command \"%s\", but did not have the minimum access level. Access Level Required: %s, Player Access Level: %s",
player.getCharacterName(), command.getName(), commandAccessLevel, playerAccessLevel);
String errorProseString1 = "use that command";
new ChatBroadcastIntent(player, new ProsePackage("StringId", new StringId("cmd_err", "state_must_have_prose"), "TO", errorProseString1, "TU", commandAccessLevel)).broadcast();
return;
}
if(!command.getCharacterAbility().isEmpty() && !player.getCreatureObject().hasAbility(command.getCharacterAbility())){
Log.i("[%s] attempted to use the command \"%s\", but did not have the required ability. Ability Required: %s",
player.getCharacterName(), command.getName(), command.getCharacterAbility());
String errorProseString = String.format("use the %s command", command.getName());
new ChatBroadcastIntent(player, new ProsePackage("StringId", new StringId("cmd_err", "ability_prose"), "TO", errorProseString)).broadcast();
return;
}
// TODO: Check if the player has the ability
// TODO: Cool-down checks
// TODO: Handle for different target
// TODO: Handle for different targetType
if (command.hasJavaCallback()) {
try {
command.getJavaCallback().newInstance().execute(galacticManager, player, target, args);
} catch (InstantiationException | IllegalAccessException e) {
Log.e(e);
}
}
else
try {
Scripts.invoke("commands/generic/" + command.getDefaultScriptCallback(), "executeCommand", galacticManager, player, target, args);
} catch (FileNotFoundException ex) {
}
}
private void loadBaseCommands() {
// 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",
"command_table_space", "client_command_table_ground", "client_command_table_space"
String [] commandTables = new String[] {
"command_table", "command_table_ground", "client_command_table",
"command_table_space", "client_command_table_ground", "client_command_table_space"
};
commandContainer.clearCommands();
@@ -321,16 +154,16 @@ public class CommandService extends Service {
}
private void loadBaseCommands(String table) {
DatatableData baseCommands = (DatatableData) ClientFactory.getInfoFromFile("datatables/command/"+table+".iff");
DatatableData baseCommands = (DatatableData) ClientFactory.getInfoFromFile("datatables/command/" + table + ".iff");
int godLevel = baseCommands.getColumnFromName("godLevel");
int cooldownGroup = baseCommands.getColumnFromName("cooldownGroup");
int cooldownGroup2 = baseCommands.getColumnFromName("cooldownGroup2");
int cooldownTime = baseCommands.getColumnFromName("cooldownTime");
int cooldownTime2 = baseCommands.getColumnFromName("cooldownTime2");
for (int row = 0; row < baseCommands.getRowCount(); row++) {
Object [] cmdRow = baseCommands.getRow(row);
Object[] cmdRow = baseCommands.getRow(row);
String commandName = ((String) cmdRow[0]).toLowerCase(Locale.ENGLISH);
if (commandContainer.isCommand(commandName))
continue; // skip duplicates - first is higher priority
@@ -340,7 +173,7 @@ public class CommandService extends Service {
command.setCrc(CRC.getCrc(commandName));
command.setDefaultPriority(DefaultPriority.getDefaultPriority((int) cmdRow[1]));
command.setScriptHook((String) cmdRow[2]);
command.setCppHook((String)cmdRow[4]);
command.setCppHook((String) cmdRow[4]);
command.setDefaultTime((float) cmdRow[6]);
command.setCharacterAbility((String) cmdRow[7]);
command.setCombatCommand(false);
@@ -350,16 +183,16 @@ public class CommandService extends Service {
command.setCooldownTime2((float) cmdRow[cooldownTime2]);
// Ziggy: The amount of columns in the table seems to change for each row
if(cmdRow.length >= 83) {
if (cmdRow.length >= 83) {
Object addToCombatQueue = cmdRow[82];
// Ziggy: Sometimes this column contains a String... uwot SOE?
if(addToCombatQueue instanceof Boolean) {
if (addToCombatQueue instanceof Boolean) {
command.setAddToCombatQueue((Boolean) addToCombatQueue);
}
}
if(godLevel >= 0){
if (godLevel >= 0) {
command.setGodLevel((int) cmdRow[godLevel]);
}
@@ -409,11 +242,11 @@ public class CommandService extends Service {
int delayAttackLoops = combatCommands.getColumnFromName("delayAttackLoops");
int delayAttackEggPosition = combatCommands.getColumnFromName("delayAttackEggPosition");
int coneLength = combatCommands.getColumnFromName("coneLength");
// animDefault anim_unarmed anim_onehandmelee anim_twohandmelee anim_polearm
// anim_pistol anim_lightRifle anim_carbine anim_rifle anim_heavyweapon
// anim_thrown anim_onehandlightsaber anim_twohandlightsaber anim_polearmlightsaber
// animDefault anim_unarmed anim_onehandmelee anim_twohandmelee anim_polearm
// anim_pistol anim_lightRifle anim_carbine anim_rifle anim_heavyweapon
// anim_thrown anim_onehandlightsaber anim_twohandlightsaber anim_polearmlightsaber
for (int row = 0; row < combatCommands.getRowCount(); row++) {
Object [] cmdRow = combatCommands.getRow(row);
Object[] cmdRow = combatCommands.getRow(row);
Command c = commandContainer.getCommand(CRC.getCrc(((String) cmdRow[0]).toLowerCase(Locale.ENGLISH)));
if (c == null)
@@ -443,24 +276,24 @@ public class CommandService extends Service {
cc.setDelayAttackLoops((int) cmdRow[delayAttackLoops]);
cc.setEggPosition(DelayAttackEggPosition.getEggPosition((int) cmdRow[delayAttackEggPosition]));
cc.setConeLength((float) cmdRow[coneLength]);
cc.setAnimations(WeaponType.UNARMED, getAnimationList((String) cmdRow[animDefault+1]));
cc.setAnimations(WeaponType.ONE_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+2]));
cc.setAnimations(WeaponType.TWO_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+3]));
cc.setAnimations(WeaponType.POLEARM_MELEE, getAnimationList((String) cmdRow[animDefault+4]));
cc.setAnimations(WeaponType.POLEARM_MELEE, getAnimationList((String) cmdRow[animDefault+5]));
cc.setAnimations(WeaponType.PISTOL, getAnimationList((String) cmdRow[animDefault+6]));
cc.setAnimations(WeaponType.LIGHT_RIFLE, getAnimationList((String) cmdRow[animDefault+7]));
cc.setAnimations(WeaponType.CARBINE, getAnimationList((String) cmdRow[animDefault+8]));
cc.setAnimations(WeaponType.RIFLE, getAnimationList((String) cmdRow[animDefault+9]));
cc.setAnimations(WeaponType.THROWN, getAnimationList((String) cmdRow[animDefault+10]));
cc.setAnimations(WeaponType.ONE_HANDED_SABER, getAnimationList((String) cmdRow[animDefault+11]));
cc.setAnimations(WeaponType.TWO_HANDED_SABER, getAnimationList((String) cmdRow[animDefault+12]));
cc.setAnimations(WeaponType.POLEARM_SABER, getAnimationList((String) cmdRow[animDefault+13]));
cc.setAnimations(WeaponType.UNARMED, getAnimationList((String) cmdRow[animDefault + 1]));
cc.setAnimations(WeaponType.ONE_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault + 2]));
cc.setAnimations(WeaponType.TWO_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault + 3]));
cc.setAnimations(WeaponType.POLEARM_MELEE, getAnimationList((String) cmdRow[animDefault + 4]));
cc.setAnimations(WeaponType.POLEARM_MELEE, getAnimationList((String) cmdRow[animDefault + 5]));
cc.setAnimations(WeaponType.PISTOL, getAnimationList((String) cmdRow[animDefault + 6]));
cc.setAnimations(WeaponType.LIGHT_RIFLE, getAnimationList((String) cmdRow[animDefault + 7]));
cc.setAnimations(WeaponType.CARBINE, getAnimationList((String) cmdRow[animDefault + 8]));
cc.setAnimations(WeaponType.RIFLE, getAnimationList((String) cmdRow[animDefault + 9]));
cc.setAnimations(WeaponType.THROWN, getAnimationList((String) cmdRow[animDefault + 10]));
cc.setAnimations(WeaponType.ONE_HANDED_SABER, getAnimationList((String) cmdRow[animDefault + 11]));
cc.setAnimations(WeaponType.TWO_HANDED_SABER, getAnimationList((String) cmdRow[animDefault + 12]));
cc.setAnimations(WeaponType.POLEARM_SABER, getAnimationList((String) cmdRow[animDefault + 13]));
commandContainer.addCommand(cc);
}
}
private String [] getAnimationList(String cell) {
private String[] getAnimationList(String cell) {
if (cell.isEmpty())
return new String[0];
return cell.split(",");
@@ -472,7 +305,7 @@ public class CommandService extends Service {
registerCallback(comand, callback);
return comand;
}
private <T extends ICmdCallback> void registerCallback(Command command, Class<T> callback) {
try {
if (callback.getConstructor() == null)
@@ -481,18 +314,17 @@ public class CommandService extends Service {
Log.e(e);
}
command.setJavaCallback(callback);
List<Command> scriptCommands = commandContainer.getScriptCommandList(command.getDefaultScriptCallback());
for(Command unregistered : scriptCommands){
if(unregistered != command && !unregistered.hasJavaCallback()){
for (Command unregistered : scriptCommands) {
if (unregistered != command && !unregistered.hasJavaCallback()) {
registerCallback(unregistered, command.getJavaCallback());
}
}
}
private void registerCallbacks() {
registerCallback("waypoint", WaypointCmdCallback.class);
registerCallback("requestWaypointAtPosition", RequestWaypointCmdCallback.class);
registerCallback("server", ServerCmdCallback.class);
@@ -519,40 +351,4 @@ public class CommandService extends Service {
registerCallback("transferItemWeapon", TransferItemCallback.class);
}
private static class QueuedCommand implements Comparable<QueuedCommand> {
private final Command command;
private final GalacticManager galacticManager;
private final SWGObject target;
private final CommandQueueEnqueue request;
public QueuedCommand(Command command, GalacticManager galacticManager, SWGObject target, CommandQueueEnqueue request) {
this.command = command;
this.galacticManager = galacticManager;
this.target = target;
this.request = request;
}
@Override
public int compareTo(QueuedCommand o) {
return command.getDefaultPriority().compareTo(o.getCommand().getDefaultPriority());
}
public Command getCommand() {
return command;
}
public GalacticManager getGalacticManager() {
return galacticManager;
}
public SWGObject getTarget() {
return target;
}
public CommandQueueEnqueue getRequest() {
return request;
}
}
}