기지 강화하기 로직 트러블 슈팅
- 기본 구상
처음에는 타워를 지정하여 하나씩 강화할 생각이었으나 남들도 다 그런식으로 할꺼라 생각하니 다른방법으로 하고싶어짐
타워를 강화하는게 아니라 기지를 강화하면 타워를 구입할시 나오는 타워의 종류가 바뀌는 방식을 채용했다.
기지 +1 === 타워 +1 을 해줌으로써 타워와 기지의 인덱스를 동일시하여 함께 올라가도록 하면 금방 끝날거라 예상하고 진행했다.
강화수치를 따로 기록할 것이 아니기때문에 따로 baseDB를 만들 필요성을 느끼지 못하여 하드코딩으로 진행했다.
let baseMaxHp = [0, 1000, 2000, 4000, 8000]; //기지 최대 체력
let upgradeIndex = 0; //기지 강화 수치 초기값
let baseHp = 1000; // 기지 체력
//기지 업그레이드 로직
function baseUpgrade() {
//업그레이드 가능 여부 체크
if (upgradeIndex < baseMaxHp.length - 1) {
upgradeIndex++;
baseHp += baseMaxHp[upgradeIndex]; // 기지 체력에 추가
console.log('기지 강화 성공');
placeBase();
} else {
console.log('최대치임');
}
}
강화시 체력을 baseMaxHp 배열로 생성해두고 특정 조건을 만족한다면 강화에 성공하는 간단한 방식으로 구현했다.
그리고 upgradeIndex를 사용하여 타워의 인덱스도 함께 변하도록 수정
async function placeNewTower() {
const upgradeTower = towerData[upgradeIndex].towerId;
const towerInfo = await towerData.find((a) => a.towerId === upgradeTower);
기지를 강화하여 upgradeIndex가 된다면 타워의 인덱스도 +1 되어 다음단계의 타워가 나온다.
하지만 이런식으로 작성하니 현재 골드검증방식과 충돌이 일어났다.
현재 골드 검증 방식은 사용한 획득한 골드 - 사용한 골드 를 서버에서 계산하는 방식이라 현재 로직으로는 클라이언트에서 모든 과정이 끝나므로 검증이 불가능한듯함
위 과정을 부분폐기하고 db에서 base의 정보를 가져와 사용하는 방식을 채용하기로함
//schema.prisma
model Base {
baseId Int @id // 베이스 ID
baselevel Int @default(1) // 베이스 초기 레벨
baseHp Int @default(200) // 베이스 HP
baseUpgradeCost Int @default(1000) // 베이스 강화 비용
img String // 베이스 이미지 파일 경로
}
스키마 파일에 base모델을 추가하고
//받은 데이터 emit으로 클라이언트에게 보내주기
socket.emit('datainfo', {
towers,
monsters,
stages,
bases,
});
socket을 사용하여 클라이언트로 base의 정보를 보내주고
let baseMaxHp = baseData[upgradeIndex].baseHp; //기지 최대 체력
if (userGold >= baseData[upgradeIndex].baseUpgradeCost) {
if (upgradeIndex < baseData.length - 1) {
upgradeIndex++;
userGold -= baseData[upgradeIndex].baseUpgradeCost;
baseHp += baseMaxHp; // 기지 체력에 추가
console.log('기지 강화 성공');
placeBase();
} else {
console.log('최대치임');
}
} else {
console.log('돈없음');
}
데이터의 값들을 db의 있는 값들로 할당시켜줘 id에따른 체력으로 변경되도록 수정하고 강화가격을 baseId마다 할당
이제 클라이언트 로직이 끝났으므로 서버에서 검증을 해줄차례
강화정보를 서버에서 저장할 수 있도록 base.model.js를 추가
//base 모델
const bases = {};
//유저의 기지 기록 배열 생성
export const createBase = (accountId) => {
bases[accountId] = [];
console.log(`createbase`);
};
//기지 기록 받아오기
export const getBases = (accountId) => {
return bases[accountId];
};
//기지 세팅
export const setBases = (accountId, basesdata) => {
return bases[accountId].push(basesdata);
};
//기지 배열 비우기
export const clearBases = (accountId) => {
console.log(`clearBases`);
bases[accountId] = [];
};
base.handler 에서 검증
import { getData } from '../init/data.js';
/**
* @desc 기지 강화 검증
* @author 우종
*/
export const baseUpgradeHandler = (accountId, data, socket) => {
//db에 기지 정보
const { bases } = getData();
// 강화를 시도할때 돈이 있었는가
const { currentUpgradeIndex, currentGold, base } = data;
if (
currentUpgradeIndex !== getBase[getBase.length - 1].currentUpgradeIndex ||
currentGold < bases[currentUpgradeIndex].upgradeCost ||
base.maxHp !== getBase[getBase.length - 1].base.maxHp
) {
return { status: 'fail', message: '너 강화못해' };
}
return { status: 'success', message: '강화성공' };
};
하지만 여기서 검증 로직을 짜다보니 상태 동기화에 대한 의문이 생김
상태 동기화는 서버에서 검증 후 클라이언트에 값을 보내주는 걸로 알고 있지만
지금 상황에서는 클라이언트에서 강화후 서버로 데이터를 보내주어 검증을 하는 방식이다.
즉 물건을 살때 선불이 아닌 후불로 결제가 이루어 지고있는것과 같음
정상적인 선불 결제를 위해 코드를 수정
import { getData } from '../init/data.js';
import {
deleteBase,
getBases,
getBaseUpgrade,
setBases,
setBaseUpgrade,
} from '../models/base.model.js';
/**
* @desc 기지 강화 검증
* @author 우종
*/
export const baseUpgradeHandler = (accountId, data, socket) => {
const { currentUpgradeIndex, currentGold, base } = data;
const getBase = getBases(accountId);
const getUpgrade = getBaseUpgrade(accountId);
if (getUpgrade.length === 0) {
setBaseUpgrade(accountId, currentUpgradeIndex);
}
if (getBase.length === 0) {
setBases(accountId, data);
return { status: 'success', message: 'Base Seting' };
}
//db에 기지 정보
const { bases } = getData();
// 강화를 시도할때 돈이 있었는가
if (
currentUpgradeIndex !== getBase[getBase.length - 1].currentUpgradeIndex ||
currentGold < bases[currentUpgradeIndex].baseUpgradeCost ||
base.maxHp !== getBase[getBase.length - 1].base.maxHp
) {
return { status: 'fail', message: 'Upgrade Fail' };
}
const index = currentUpgradeIndex + 1;
const gold = currentGold - bases[currentUpgradeIndex].baseUpgradeCost;
const hp = base.maxHp + bases[currentUpgradeIndex].baseHp;
setBaseUpgrade(accountId, index);
console.log(getBaseUpgrade(accountId)); // 이거 가져다 골드 검증하면됨
deleteBase(accountId, 0);
socket.emit('base', {
index,
gold,
hp,
});
return { status: 'success', message: 'Upgrade Success' };
클라이언트에서 기지가 생성될 경우의 초기값을 받아오고 현재 유저가 가지고있는 돈이 db에 정의된 강화 비용과 같은지 비교하고 현재 인덱스를 비교하여 조건이 만족할 경우 socket.emit으로 클라이언트로 강화된 후의 값을 전달한다..
serverSocket.on('base', (data) => {
upgradeIndex = data.index;
userGold = data.gold;
baseHp = data.hp;
placeBase();
});
클라이언트에서는 해당 값을 받아 변수에 할당시키고 할당된 값을 다시 그려준다.
//최종 강화 로직
function baseUpgrade() {
let baseMaxHp = baseData[upgradeIndex].baseHp; //기지 최대 체력
//업그레이드 가능 여부 체크
if (userGold >= baseData[upgradeIndex].baseUpgradeCost) {
if (upgradeIndex < baseData.length - 1) {
//서버로 쏴주는 부분
serverSocket.emit('event', {
handlerId: 35,
currentUpgradeIndex: upgradeIndex, //현재 강화 단계
currentGold: userGold, //현재 골드
base: base,
});
console.log('기지 강화 성공');
// placeBase();
} else {
console.log('최대치임');
}
} else {
console.log('돈없음');
}
}
마지막으로 골드 검증을 위해 모델을 추가하여 남겨둠
//최종 base 모델
const bases = {};
const baseUpgrade = {};
//유저의 기지 기록 배열 생성
export const createBase = (accountId) => {
bases[accountId] = [];
baseUpgrade[accountId] = [];
console.log(`createbase`);
};
//기지 기록 받아오기
export const getBases = (accountId) => {
return bases[accountId];
};
//기지 업글 단계
export const getBaseUpgrade = (accountId) => {
return baseUpgrade[accountId];
};
//기지 세팅
export const setBases = (accountId, basesdata) => {
return bases[accountId].push(basesdata);
};
//기지 업글 단계 저장
export const setBaseUpgrade = (accountId, upgrade) => {
return baseUpgrade[accountId].push(upgrade);
};
//유저 특정 기지 제거
export const deleteBase = (accountId, selectedBaseIndex) => {
console.log(`deleteBase`);
return bases[accountId].splice(selectedBaseIndex, 1);
};
//기지 배열 비우기
export const clearBases = (accountId) => {
console.log(`clearBases`);
bases[accountId] = [];
};