Merge pull request #674 from madsboddum/Holocore-671

Holocore-671 Previous learned and dropped ability can still be execut…
This commit is contained in:
Josh Larson
2022-07-20 14:14:21 -05:00
committed by GitHub
11 changed files with 145 additions and 44 deletions
@@ -47,7 +47,6 @@ import com.projectswg.holocore.resources.support.objects.swg.player.PlayerObject
import com.projectswg.holocore.resources.support.objects.swg.tangible.TangibleObject;
import com.projectswg.holocore.resources.support.objects.swg.weapon.DefaultWeaponFactory;
import com.projectswg.holocore.resources.support.objects.swg.weapon.WeaponObject;
import com.projectswg.holocore.resources.support.objects.swg.weapon.WeaponType;
import com.projectswg.holocore.services.support.objects.ObjectStorageService.BuildingLookup;
import me.joshlarson.jlcommon.utilities.Arguments;
import org.jetbrains.annotations.NotNull;
@@ -61,12 +60,10 @@ public class CharacterCreation {
private final Player player;
private final ClientCreateCharacter create;
private final String biography;
public CharacterCreation(Player player, ClientCreateCharacter create, String biography) {
public CharacterCreation(Player player, ClientCreateCharacter create) {
this.player = player;
this.create = create;
this.biography = biography;
}
public CreatureObject createCharacter(AccessLevel accessLevel, ZoneInsertion info) {
@@ -79,7 +76,7 @@ public class CharacterCreation {
createHair(creatureObj, create.getHair(), create.getHairCustomization());
createStarterClothing(creatureObj, race, create.getClothes());
playerObj.setAdminTag(accessLevel);
playerObj.setBiography(biography);
playerObj.setBiography(create.getBiography());
ObjectCreatedIntent.broadcast(playerObj);
ObjectCreatedIntent.broadcast(creatureObj);
@@ -165,6 +162,9 @@ public class CharacterCreation {
// new GrantSkillIntent(GrantSkillIntent.IntentType.GRANT, "crafting_artisan_novice", creatureObj, true).broadcast();
new GrantSkillIntent(GrantSkillIntent.IntentType.GRANT, "combat_brawler_novice", creatureObj, true).broadcast();
// new GrantSkillIntent(GrantSkillIntent.IntentType.GRANT, "combat_marksman_novice", creatureObj, true).broadcast();
// Everyone can Burst Run
creatureObj.addCommand("burstrun");
Collection<String> languages = languagesSkillsForRace(creatureObj.getRace());
@@ -38,23 +38,30 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class CommandQueueService extends Service {
private final ScheduledThreadPool executor;
private final Map<CreatureObject, CreatureCombatQueue> combatQueueMap;
private final CombatCommandHandler combatCommandHandler;
private final long delayBetweenCheckingCommandQueue;
public CommandQueueService() {
this(100);
}
public CommandQueueService(long delayBetweenCheckingCommandQueue) {
this.executor = new ScheduledThreadPool(4, "command-queue-%d");
this.combatQueueMap = new ConcurrentHashMap<>();
this.combatCommandHandler = new CombatCommandHandler();
this.delayBetweenCheckingCommandQueue = delayBetweenCheckingCommandQueue;
}
@Override
public boolean initialize() {
executor.start();
executor.executeWithFixedRate(0, 100, this::executeQueuedCommands);
executor.executeWithFixedRate(0, delayBetweenCheckingCommandQueue, this::executeQueuedCommands);
return true;
}
@@ -331,6 +338,15 @@ public class CommandQueueService extends Service {
}
if (rootCommand instanceof CombatCommand combatCommand) {
Collection<String> sourceCommands = source.getCommands()
.stream()
.map(sourceCommand -> sourceCommand.toLowerCase(Locale.US))
.collect(Collectors.toSet());
if (!sourceCommands.contains(rootCommand.getName())) {
return new CheckCommandResult(ErrorCode.ABILITY, 0);
}
if (combatCommand.getHitType() == HitType.HEAL && combatCommand.getAttackType() == AttackType.SINGLE_TARGET) {
SWGObject target;
switch (combatCommand.getTargetType()) {
@@ -169,23 +169,17 @@ public class CharacterCreationService extends Service {
}
private void sendCharCreationFailure(Player player, ClientCreateCharacter create, ErrorMessage err, String actualReason) {
NameFailureReason reason = NameFailureReason.NAME_SYNTAX;
switch (err) {
case NAME_DECLINED_INTERNAL_ERROR:
case NAME_APPROVED:
reason = NameFailureReason.NAME_RETRY;
break;
case NAME_DECLINED_FICTIONALLY_INAPPROPRIATE:
reason = NameFailureReason.NAME_FICTIONALLY_INAPPRORIATE;
break;
case NAME_DECLINED_IN_USE: reason = NameFailureReason.NAME_IN_USE; break;
case NAME_DECLINED_EMPTY: reason = NameFailureReason.NAME_DECLINED_EMPTY; break;
case NAME_DECLINED_RESERVED: reason = NameFailureReason.NAME_DEV_RESERVED; break;
case NAME_DECLINED_TOO_FAST: reason = NameFailureReason.NAME_TOO_FAST; break;
case SERVER_CHARACTER_CREATION_MAX_CHARS: reason = NameFailureReason.TOO_MANY_CHARACTERS; break;
default:
break;
}
NameFailureReason reason = switch (err) {
case NAME_DECLINED_INTERNAL_ERROR, NAME_APPROVED -> NameFailureReason.NAME_RETRY;
case NAME_DECLINED_FICTIONALLY_INAPPROPRIATE -> NameFailureReason.NAME_FICTIONALLY_INAPPRORIATE;
case NAME_DECLINED_IN_USE -> NameFailureReason.NAME_IN_USE;
case NAME_DECLINED_EMPTY -> NameFailureReason.NAME_DECLINED_EMPTY;
case NAME_DECLINED_RESERVED -> NameFailureReason.NAME_DEV_RESERVED;
case NAME_DECLINED_TOO_FAST -> NameFailureReason.NAME_TOO_FAST;
case SERVER_CHARACTER_CREATION_MAX_CHARS -> NameFailureReason.TOO_MANY_CHARACTERS;
default -> NameFailureReason.NAME_SYNTAX;
};
StandardLog.onPlayerError(this, player, "failed to create character '%s' with server error [%s] from %s", create.getName(), actualReason, create.getSocketAddress());
player.sendPacket(new CreateCharacterFailure(reason));
}
@@ -225,7 +219,7 @@ public class CharacterCreationService extends Service {
Log.e("Failed to get spawn information for location: " + spawnLocation);
return null;
}
CharacterCreation creation = new CharacterCreation(player, create, create.getBiography());
CharacterCreation creation = new CharacterCreation(player, create);
return creation.createCharacter(player.getAccessLevel(), info);
}
@@ -24,7 +24,7 @@ class EquipArmorTest : TestRunnerSimulatedWorld() {
@BeforeEach
fun setUp() {
registerService(CommandQueueService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
registerService(SkillService())
}
@@ -22,7 +22,7 @@ class EquipClothingTest : TestRunnerSimulatedWorld() {
@BeforeEach
fun setUp() {
registerService(CommandQueueService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
}
@@ -29,7 +29,7 @@ class EquipLightsaberTest : TestRunnerSimulatedWorld() {
@BeforeEach
internal fun setUp() {
registerService(CommandQueueService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
registerService(SkillService())
registerService(LightsaberService())
@@ -22,7 +22,7 @@ class EquipWeaponTest : TestRunnerSimulatedWorld() {
@BeforeEach
fun setUp() {
registerService(CommandQueueService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
}
@@ -30,7 +30,7 @@ class LightsaberInventoryTest : TestRunnerSimulatedWorld() {
@BeforeEach
internal fun setUp() {
registerService(CommandQueueService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
registerService(SkillService())
registerService(LightsaberService())
@@ -0,0 +1,78 @@
package com.projectswg.holocore.services.gameplay.combat
import com.projectswg.common.data.CRC
import com.projectswg.common.network.packets.swg.zone.object_controller.CommandQueueEnqueue
import com.projectswg.common.network.packets.swg.zone.object_controller.CommandTimer
import com.projectswg.holocore.intents.gameplay.player.experience.skills.GrantSkillIntent
import com.projectswg.holocore.intents.support.global.network.InboundPacketIntent
import com.projectswg.holocore.intents.support.global.network.OutboundPacketIntent
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent
import com.projectswg.holocore.resources.support.objects.ObjectCreator
import com.projectswg.holocore.resources.support.objects.swg.weapon.DefaultWeaponFactory
import com.projectswg.holocore.services.gameplay.player.experience.skills.SkillService
import com.projectswg.holocore.services.support.global.commands.CommandExecutionService
import com.projectswg.holocore.services.support.global.commands.CommandQueueService
import com.projectswg.holocore.test.resources.GenericCreatureObject
import com.projectswg.holocore.test.resources.GenericPlayer
import com.projectswg.holocore.test.runners.TestRunnerSimulatedWorld
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class AbilityTest : TestRunnerSimulatedWorld() {
@BeforeEach
fun setup() {
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
registerService(SkillService())
}
@Test
fun `you can execute an ability when you have the skill that grants it`() {
val creatureObject = createCreatureObject()
val player = creatureObject.owner ?: throw RuntimeException("Unable to access player")
grantRequiredSkillForParryRiposte(creatureObject)
val success = parryRiposte(player)
assertTrue(success)
}
@Test
fun `you cannot execute an ability when you are missing the skill that grants it`() {
val creatureObject = createCreatureObject()
val player = creatureObject.owner ?: throw RuntimeException("Unable to access player")
val success = parryRiposte(player)
assertFalse(success)
}
private fun grantRequiredSkillForParryRiposte(creatureObject: GenericCreatureObject) {
GrantSkillIntent.broadcast(GrantSkillIntent.IntentType.GRANT, "combat_brawler_master", creatureObject, true)
waitForIntents()
}
private fun parryRiposte(player: GenericPlayer): Boolean {
val crc = CRC.getCrc("parryriposte")
InboundPacketIntent.broadcast(player, CommandQueueEnqueue(player.creatureObject.objectId, 0, crc, 0, ""))
val commandTimer = player.waitForNextPacket(CommandTimer::class.java)
if (commandTimer != null) {
return commandTimer.flags.contains(CommandTimer.CommandTimerFlag.EXECUTE)
} else {
throw RuntimeException("No CommandTimer packet was sent at all")
}
}
private fun createCreatureObject(): GenericCreatureObject {
val creatureObject = GenericCreatureObject(ObjectCreator.getNextObjectId())
ObjectCreatedIntent.broadcast(creatureObject)
val defaultWeapon = DefaultWeaponFactory.createDefaultWeapon()
defaultWeapon.moveToContainer(creatureObject)
creatureObject.equippedWeapon = defaultWeapon
return creatureObject
}
}
@@ -1,15 +1,17 @@
package com.projectswg.holocore.services.gameplay.combat
import com.projectswg.common.data.CRC
import com.projectswg.common.network.packets.swg.login.creation.ClientCreateCharacter
import com.projectswg.common.network.packets.swg.zone.object_controller.CommandQueueEnqueue
import com.projectswg.holocore.intents.support.global.network.InboundPacketIntent
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent
import com.projectswg.holocore.resources.support.objects.ObjectCreator
import com.projectswg.holocore.resources.support.objects.swg.weapon.DefaultWeaponFactory
import com.projectswg.holocore.services.gameplay.combat.buffs.BuffService
import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader
import com.projectswg.holocore.resources.support.global.player.AccessLevel
import com.projectswg.holocore.resources.support.global.player.Player
import com.projectswg.holocore.resources.support.global.zone.creation.CharacterCreation
import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject
import com.projectswg.holocore.services.support.global.commands.CommandExecutionService
import com.projectswg.holocore.services.support.global.commands.CommandQueueService
import com.projectswg.holocore.test.resources.GenericCreatureObject
import com.projectswg.holocore.test.resources.GenericPlayer
import com.projectswg.holocore.test.runners.TestRunnerSimulatedWorld
import org.junit.jupiter.api.Assertions.assertFalse
@@ -22,7 +24,7 @@ class AttackCostTest : TestRunnerSimulatedWorld() {
@BeforeEach
fun setup() {
registerService(BuffService())
registerService(CommandQueueService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
registerService(CombatStatusService())
}
@@ -90,19 +92,26 @@ class AttackCostTest : TestRunnerSimulatedWorld() {
assertFalse(creatureObject.hasBuff("burstRun"))
}
private fun burstRun(player: GenericPlayer) {
private fun burstRun(player: Player) {
val crc = CRC.getCrc("burstrun")
broadcastAndWait(InboundPacketIntent(player, CommandQueueEnqueue(player.creatureObject.objectId, 0, crc, 0, "")))
Thread.sleep(150) // Give the command queue a chance to be processed
Thread.sleep(10) // Give the command queue a chance to be processed
}
private fun createCreatureObject(): GenericCreatureObject {
val creatureObject = GenericCreatureObject(ObjectCreator.getNextObjectId())
ObjectCreatedIntent.broadcast(creatureObject)
val defaultWeapon = DefaultWeaponFactory.createDefaultWeapon()
defaultWeapon.moveToContainer(creatureObject)
creatureObject.equippedWeapon = defaultWeapon
private fun createCreatureObject(): CreatureObject {
val player = GenericPlayer()
val clientCreateCharacter = ClientCreateCharacter()
clientCreateCharacter.biography = ""
clientCreateCharacter.clothes = "combat_brawler"
clientCreateCharacter.race = "object/creature/player/shared_human_male.iff"
clientCreateCharacter.name = "Testing Character"
val characterCreation = CharacterCreation(player, clientCreateCharacter)
val mosEisley = DataLoader.zoneInsertions().getInsertion("tat_moseisley")
val creatureObject = characterCreation.createCharacter(AccessLevel.PLAYER, mosEisley)
creatureObject.owner = player
return creatureObject
}
@@ -4,6 +4,7 @@ import com.projectswg.common.data.CRC
import com.projectswg.common.data.encodables.tangible.PvpStatus
import com.projectswg.common.network.packets.swg.zone.object_controller.CommandQueueEnqueue
import com.projectswg.holocore.intents.gameplay.gcw.faction.FactionIntent
import com.projectswg.holocore.intents.gameplay.player.experience.skills.GrantSkillIntent
import com.projectswg.holocore.intents.support.global.network.InboundPacketIntent
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent
import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData
@@ -12,6 +13,7 @@ import com.projectswg.holocore.resources.support.objects.ObjectCreator
import com.projectswg.holocore.resources.support.objects.swg.SWGObject
import com.projectswg.holocore.resources.support.objects.swg.weapon.DefaultWeaponFactory
import com.projectswg.holocore.services.gameplay.faction.FactionFlagService
import com.projectswg.holocore.services.gameplay.player.experience.skills.SkillService
import com.projectswg.holocore.services.support.global.commands.CommandExecutionService
import com.projectswg.holocore.services.support.global.commands.CommandQueueService
import com.projectswg.holocore.test.resources.GenericCreatureObject
@@ -25,7 +27,8 @@ class FactionPvpTest : TestRunnerSimulatedWorld() {
@BeforeEach
fun setup() {
registerService(CommandQueueService())
registerService(SkillService())
registerService(CommandQueueService(5))
registerService(CommandExecutionService())
registerService(FactionFlagService())
registerService(CombatStatusService())
@@ -66,11 +69,12 @@ class FactionPvpTest : TestRunnerSimulatedWorld() {
val targetObjectId = target.objectId
broadcastAndWait(InboundPacketIntent(player, CommandQueueEnqueue(player.creatureObject.objectId, 0, crc, targetObjectId, "")))
Thread.sleep(150) // Give the command queue a chance to be processed
Thread.sleep(10) // Give the command queue a chance to be processed
}
private fun createCreatureObject(): GenericCreatureObject {
val creatureObject = GenericCreatureObject(ObjectCreator.getNextObjectId())
GrantSkillIntent.broadcast(GrantSkillIntent.IntentType.GRANT, "species_human", creatureObject, true)
ObjectCreatedIntent.broadcast(creatureObject)
val defaultWeapon = DefaultWeaponFactory.createDefaultWeapon()
defaultWeapon.moveToContainer(creatureObject)