Skip to content

Commit

Permalink
Fix allowance to duplicated heroes
Browse files Browse the repository at this point in the history
    Potential fix random bug caused for allowing multiple different locks for the same thread
    Tag v0.11.6
  • Loading branch information
KevinGuancheDarias committed Feb 27, 2024
1 parent fa4d4ca commit ce22da0
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 78 deletions.
9 changes: 6 additions & 3 deletions business/database/migrations/v0.11.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ CREATE TABLE track_browser
json_content TEXT NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB
) ENGINE = InnoDB;

UPDATE units SET attack = 0 WHERE attack is null;
ALTER TABLE `units` CHANGE `attack` `attack` SMALLINT UNSIGNED NOT NULL DEFAULT '0';
UPDATE units
SET attack = 0
WHERE attack is null;
ALTER TABLE `units`
CHANGE `attack` `attack` SMALLINT UNSIGNED NOT NULL DEFAULT '0';
2 changes: 1 addition & 1 deletion business/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</parent>
<groupId>com.kevinguanchedarias.owge</groupId>
<artifactId>owgejava-backend</artifactId>
<version>0.11.5-SNAPSHOT</version>
<version>0.11.6-SNAPSHOT</version>
<name>OWGE Business</name>
<url>http://owgejava.kevinguanchedarias.com</url>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.kevinguanchedarias.owgejava.business.user.UserSessionService;
import com.kevinguanchedarias.owgejava.business.user.listener.UserDeleteListener;
import com.kevinguanchedarias.owgejava.business.util.TransactionUtilService;
import com.kevinguanchedarias.owgejava.dto.RunningUnitBuildDto;
import com.kevinguanchedarias.owgejava.dto.RunningUpgradeDto;
import com.kevinguanchedarias.owgejava.entity.*;
import com.kevinguanchedarias.owgejava.enumerations.MissionType;
Expand Down Expand Up @@ -200,66 +199,66 @@ public void processLevelUpAnUpgrade(Long missionId) {
*
* @author Kevin Guanche Darias
*/
@Transactional
public RunningUnitBuildDto registerBuildUnit(Integer userId, Long planetId, Integer unitId, Long count) {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void registerBuildUnit(Integer userId, Long planetId, Integer unitId, Long count) {
planetCheckerService.myCheckIsOfUserProperty(planetId);
checkUnitBuildMissionDoesNotExists(userId, planetId);
var relation = objectRelationBo.findOne(ObjectEnum.UNIT,
unitId);
checkUnlockedUnit(userId, relation);
var user = SpringRepositoryUtil.findByIdOrDie(userStorageRepository, userId);
missionBaseService.checkMissionLimitNotReached(user);
var unit = unitBo.findByIdOrDie(unitId);
Long finalCount = Boolean.TRUE.equals(unit.getIsUnique()) ? 1 : count;
unitBo.checkIsUniqueBuilt(user, unit);
ResourceRequirementsPojo resourceRequirements = unitBo.calculateRequirements(unit, finalCount);
if (!resourceRequirements.canRun(user, userEnergyServiceBo)) {
throw new SgtMissionRegistrationException("No enough resources!");
}
resourceRequirements
.setRequiredTime(improvementBo.computeImprovementValue(resourceRequirements.getRequiredTime(),
improvementBo.findUserImprovement(user).getMoreUnitBuildSpeed(), false));
unitTypeBo.checkWouldReachUnitTypeLimit(user, unit.getType().getId(), finalCount);
var missionInformation = new MissionInformation();
missionInformation.setRelation(relation);
missionInformation.setValue(planetId.doubleValue());

var mission = new Mission();
mission.setStartingDate(LocalDateTime.now(ZoneOffset.UTC));
mission.setMissionInformation(missionInformation);
if (configurationBo.findOrSetDefault("ZERO_BUILD_TIME", "TRUE").getValue().equals("TRUE")) {
resourceRequirements.setRequiredTime(3D);
}
attachRequirementsToMission(mission, resourceRequirements);
planetLockUtilService.doInsideLockById(List.of(planetId), () -> {
checkUnitBuildMissionDoesNotExists(userId, planetId);
var relation = objectRelationBo.findOne(ObjectEnum.UNIT,
unitId);
checkUnlockedUnit(userId, relation);
var user = SpringRepositoryUtil.findByIdOrDie(userStorageRepository, userId);
missionBaseService.checkMissionLimitNotReached(user);
var unit = unitBo.findByIdOrDie(unitId);
Long finalCount = Boolean.TRUE.equals(unit.getIsUnique()) ? 1 : count;
unitBo.checkIsUniqueBuilt(user, unit);
ResourceRequirementsPojo resourceRequirements = unitBo.calculateRequirements(unit, finalCount);
if (!resourceRequirements.canRun(user, userEnergyServiceBo)) {
throw new SgtMissionRegistrationException("No enough resources!");
}
resourceRequirements
.setRequiredTime(improvementBo.computeImprovementValue(resourceRequirements.getRequiredTime(),
improvementBo.findUserImprovement(user).getMoreUnitBuildSpeed(), false));
unitTypeBo.checkWouldReachUnitTypeLimit(user, unit.getType().getId(), finalCount);
var missionInformation = new MissionInformation();
missionInformation.setRelation(relation);
missionInformation.setValue(planetId.doubleValue());

var mission = new Mission();
mission.setStartingDate(LocalDateTime.now(ZoneOffset.UTC));
mission.setMissionInformation(missionInformation);
if (configurationBo.findOrSetDefault("ZERO_BUILD_TIME", "TRUE").getValue().equals("TRUE")) {
resourceRequirements.setRequiredTime(3D);
}
attachRequirementsToMission(mission, resourceRequirements);

mission.setType(missionTypeBo.find(MissionType.BUILD_UNIT));
mission.setUser(user);
missionInformation.setMission(mission);
mission.setType(missionTypeBo.find(MissionType.BUILD_UNIT));
mission.setUser(user);
missionInformation.setMission(mission);

substractResources(user, mission);
substractResources(user, mission);

userStorageRepository.save(user);
missionRepository.save(mission);
userStorageRepository.save(user);
missionRepository.save(mission);

var obtainedUnit = new ObtainedUnit();
obtainedUnit.setMission(mission);
obtainedUnit.setCount(finalCount);
obtainedUnit.setUnit(unit);
obtainedUnit.setUser(user);
obtainedUnitRepository.save(obtainedUnit);
var obtainedUnit = new ObtainedUnit();
obtainedUnit.setMission(mission);
obtainedUnit.setCount(finalCount);
obtainedUnit.setUnit(unit);
obtainedUnit.setUser(user);
obtainedUnitRepository.save(obtainedUnit);

missionSchedulerService.scheduleMission(mission);
missionSchedulerService.scheduleMission(mission);

transactionUtilService.doAfterCommit(() -> {
entityManager.refresh(obtainedUnit);
entityManager.refresh(mission);
emitMissionCountChange(userId);
missionEventEmitterBo.emitUnitBuildChange(userId);
unitTypeBo.emitUserChange(userId);
userEventEmitterBo.emitUserData(user);
transactionUtilService.doAfterCommit(() -> {
entityManager.refresh(obtainedUnit);
entityManager.refresh(mission);
emitMissionCountChange(userId);
missionEventEmitterBo.emitUnitBuildChange(userId);
unitTypeBo.emitUserChange(userId);
userEventEmitterBo.emitUserData(user);
});
});

return new RunningUnitBuildDto(unit, mission, planetBo.findById(planetId), finalCount);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ public void doInsideLock(Set<String> wantedKeys, Runnable runnable) {
private void tryGainLock(
List<String> keysAsList, PreparedStatementCallback<String> preparedStatementCallback, Runnable action, int times
) {
String result = jdbcTemplate.execute(generateSql("GET_LOCK(?,?)", keysAsList), preparedStatementCallback);
String result = doLock(keysAsList, preparedStatementCallback);
if (result == null) {
throw new IllegalStateException("result can't be null");
doReleaseLock(keysAsList);
tryGainLock(keysAsList, preparedStatementCallback, action, times + 1);
} else {
int acquiredLocks = Arrays.stream(result.split(",")).mapToInt(Integer::valueOf).reduce(0, Integer::sum);
if (acquiredLocks == keysAsList.size()) {
Expand All @@ -85,6 +86,19 @@ private void tryGainLock(
}
}

private String doLock(List<String> keysAsList, PreparedStatementCallback<String> preparedStatementCallback) {
try {
return jdbcTemplate.execute(generateSql("GET_LOCK(?,?)", keysAsList), preparedStatementCallback);
} catch (Exception e) {
if (e.getMessage().contains("Deadlock")) {
log.debug("Handling deadlock");
return null;
} else {
throw e;
}
}
}

private PreparedStatementCallback<String> releaseLockLambda(List<String> keysAsList) {
return ps -> {
generateBindParamsForReleaseLock(keysAsList, ps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ void registerLevelUpAnUpgrade_should_work(String zeroTimeConfigValue, double exp
void registerBuildUnit_should_throw_if_mission_already_going() {
given(missionFinderBo.findRunningUnitBuild(USER_ID_1, (double) SOURCE_PLANET_ID))
.willReturn(mock(RunningUnitBuildDto.class));
doAnswer(new InvokeRunnableLambdaAnswer(1)).when(planetLockUtilService).doInsideLockById(anyList(), any());

assertThatThrownBy(() -> missionBo.registerBuildUnit(USER_ID_1, SOURCE_PLANET_ID, UNIT_ID_1, OBTAINED_UNIT_1_COUNT))
.isInstanceOf(SgtBackendUnitBuildAlreadyRunningException.class);
Expand All @@ -362,6 +363,7 @@ void registerBuildUnit_should_throw_if_no_resources(boolean isUnique, long targe
given(userStorageRepository.findById(USER_ID_1)).willReturn(Optional.of(user));
given(missionRepository.countByUserIdAndResolvedFalse(USER_ID_1)).willReturn(runningCount);
givenMaxMissionsCount(user);
doAnswer(new InvokeRunnableLambdaAnswer(1)).when(planetLockUtilService).doInsideLockById(anyList(), any());

given(objectRelationBo.findOne(ObjectEnum.UNIT, UNIT_ID_1)).willReturn(relation);
given(unitBo.findByIdOrDie(UNIT_ID_1)).willReturn(unit);
Expand Down Expand Up @@ -413,16 +415,13 @@ void registerBuildUnit_should_work(String zeroTimeConfigValue, double expectedTi
given(missionTypeRepository.findOneByCode(MissionType.BUILD_UNIT.name()))
.willReturn(Optional.of(missionType));
doAnswer(new InvokeRunnableLambdaAnswer(0)).when(transactionUtilService).doAfterCommit(any());
doAnswer(new InvokeRunnableLambdaAnswer(1)).when(planetLockUtilService).doInsideLockById(anyList(), any());
given(planetBo.findById(SOURCE_PLANET_ID)).willReturn(givenSourcePlanet());
given(missionTypeBo.find(MissionType.BUILD_UNIT)).willReturn(MissionTypeMock.givenMissinType(MissionType.BUILD_UNIT));

var result = missionBo.registerBuildUnit(USER_ID_1, SOURCE_PLANET_ID, UNIT_ID_1, OBTAINED_UNIT_1_COUNT);
missionBo.registerBuildUnit(USER_ID_1, SOURCE_PLANET_ID, UNIT_ID_1, OBTAINED_UNIT_1_COUNT);

verify(missionBaseService, times(1)).checkMissionLimitNotReached(user);
assertThat(result.getRequiredTime()).isEqualTo(expectedTime);
assertThat(result.getRequiredPrimary()).isEqualTo(resourceRequirements.getRequiredPrimary());
assertThat(result.getRequiredSecondary()).isEqualTo(resourceRequirements.getRequiredSecondary());
assertThat(result.getType()).isEqualTo(MissionType.BUILD_UNIT);
verify(userStorageRepository, times(1)).save(user);
verify(missionRepository, times(1)).save(any());
var captor = ArgumentCaptor.forClass(ObtainedUnit.class);
Expand Down Expand Up @@ -455,7 +454,6 @@ void findRunningLevelUpMission_should_work(Improvement improvement, int timesHib
assertThat(retVal.getUpgrade().getId()).isEqualTo(UPGRADE_ID);
assertThat(retVal.getMissionId()).isEqualTo(UPGRADE_MISSION_ID);
}

}

@Test
Expand Down
5 changes: 5 additions & 0 deletions game-frontend/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

# OWGE changelog

v0.11.6 (2024-02-27 20:10)
===========================
* __Fix:__ Fix allowance of duplicating units
* __Fix:__ Fix potential random bug, caused by the tolerance of the lock util to multiple invocations of lock for the same thread, if this fix doesn't work, can be good to consider rolling bck MysqlLockUtilService to v0.11.4

v0.11.5 (2024-02-27 04:39)
============================
* [class=Developer] __Improvement:__ Migrate to Spring Boot 3.2
Expand Down
2 changes: 1 addition & 1 deletion game-frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "game-frontend",
"version": "0.11.5",
"version": "0.11.6",
"license": "MIT",
"angular-cli": {},
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions game-rest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
</parent>
<groupId>com.kevinguanchedarias.owgejava</groupId>
<artifactId>game-rest</artifactId>
<version>0.11.5-SNAPSHOT</version>
<version>0.11.6-SNAPSHOT</version>
<name>OWGE Game Rest</name>

<properties>
<owge.version>0.11.5-SNAPSHOT</owge.version>
<owge.version>0.11.6-SNAPSHOT</owge.version>
<sonar.projectKey>KevinGuancheDarias_owge</sonar.projectKey>
<sonar.moduleKey>${project.groupId}:${project.artifactId}</sonar.moduleKey>
<sonar.organization>kevinguanchedarias</sonar.organization>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import com.kevinguanchedarias.owgejava.interfaces.SyncSource;
import com.kevinguanchedarias.owgejava.pojo.DeprecationRestResponse;
import com.kevinguanchedarias.owgejava.pojo.UnitWithRequirementInformation;
import com.kevinguanchedarias.owgejava.repository.MissionRepository;
import com.kevinguanchedarias.owgejava.responses.CriticalAttackInformationResponse;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
Expand All @@ -38,7 +37,6 @@ public class UnitRestService implements SyncSource {
private final UnitBo unitBo;
private final CriticalAttackBo criticalAttackBo;
private final ObtainedUnitFinderBo obtainedUnitFinderBo;
private final MissionRepository missionRepository;

/**
* @author Kevin Guanche Darias <[email protected]>
Expand All @@ -56,15 +54,10 @@ public Object findRunning(@RequestParam("planetId") Double planetId) {
}

@PostMapping(value = "build")
public Object build(@RequestParam("planetId") Long planetId, @RequestParam("unitId") Integer unitId,
@RequestParam("count") Long count) {
public void build(@RequestParam("planetId") Long planetId, @RequestParam("unitId") Integer unitId,
@RequestParam("count") Long count) {
Integer userId = findLoggedInUser().getId();
RunningUnitBuildDto retVal = missionBo.registerBuildUnit(userId, planetId, unitId, count);
if (retVal == null) {
return "";
}
retVal.setMissionsCount(missionRepository.countByUserIdAndResolvedFalse(userId));
return retVal;
missionBo.registerBuildUnit(userId, planetId, unitId, count);
}

@GetMapping("cancel")
Expand Down

0 comments on commit ce22da0

Please sign in to comment.