-
Notifications
You must be signed in to change notification settings - Fork 0
6. Code Arena
JIHUN edited this page Sep 19, 2023
·
1 revision
- 사용자가 방을 생성할 때, 방장인지 식별할 수 있는 값 생성(room_host)
- 방을 입장할 때 room_host와 입장하는 사용자 이름이 동일하다면 방장으로 인식합니다.
- 사용자가 방에 입장 시 사용자 목록 탭에 추가되고 방장 여부에 따라 '방장' 또는 '일반'으로 나뉩니다.
- 방장은 게임을 시작할 수있는 Start 버튼이 활성화되고, '일반'은 준비를 할 수있는 Ready 버튼이 활성화 됩니다.
방 생성시 실행되는 방장 권한 부여 코드 (클라이언트)
codeArenaList.js (line 65)
arenaSocket.emit("check_admin", { nickname: res.data });
// 방 생성시 방장 권한 부여
arenaSocket.on("admin_status", ({ isAdmin }) => {
if (isAdmin) {
arenaSocket["isAdmin"] = isAdmin;
buttonDiv.style.display = "block";
$startBtn.style.display = "block";
}
});
방 생성시 실행되는 방장 권한 부여 코드 (서버)
server.js (line 127)
socket.on("check_admin", (nickname) => {
let isAdmin = false;
if (nickname.nickname == roomInfo.createdBy) {
isAdmin = true;
socket["isAdmin"] = true;
} else {
isAdmin = false;
}
socket.emit("admin_status", { isAdmin });
});
사용자 입장 시 사용자 목록 탭 업데이트 함수
codeArenaList.js (line 606)
const updateArenaNickname = (conn_user, room_host, room_number) => {
const $c_a_p_user = document.querySelector(".c_a_p_user");
conn_user.forEach((userInfo) => {
const newUser = document.createElement("div");
if (userInfo.ROOM_NUMBER == room_number) {
if (room_host == userInfo.CONN_USER) {
// 들어오는 사람이 방을 만든 사람의 닉네임과 같다면? = 방장일 때
newUser.className = `c_a_p_u1`;
newUser.innerHTML += `
<div class="u_info">
<div class="u_i_img">방장</div>
<div class="u_i_nick" data-user="${userInfo.CONN_USER}">${userInfo.CONN_USER}</div>
</div>
<div class="u_remain">
<div div class="u_r_ques">
<div class="u_r_circle ${userInfo.CONN_USER}" style="display:none;">ok</div>
</div>
</div>
`;
$c_a_p_user.append(newUser);
} else {
if (userInfo.USER_READY == "N") {
// 준비 안했을 때
// 들어오는 사람이 방을 만든 사람의 닉네임과 같다면? = 일반일 때
newUser.className = `c_a_p_u2`;
newUser.innerHTML += `
<div class="u_info">
<div class="u_i_img">일반</div>
<div class="u_i_nick normal_user" data-user="${userInfo.CONN_USER}">${userInfo.CONN_USER}</div>
</div>
<div class="u_remain">
<div div class="u_r_ques">
<div class="u_r_circle ${userInfo.CONN_USER}" style="display:none;">READY</div>
</div>
</div>
`;
$c_a_p_user.append(newUser);
} else {
// 기존에 준비했던 사람
// 들어오는 사람이 방을 만든 사람의 닉네임과 같다면? = 일반일 때
newUser.className = `c_a_p_u2`;
newUser.innerHTML += `
<div class="u_info">
<div class="u_i_img">일반</div>
<div class="u_i_nick normal_user" data-user="${userInfo.CONN_USER}">${userInfo.CONN_USER}</div>
</div>
<div class="u_remain">
<div div class="u_r_ques">
<div class="u_r_circle ${userInfo.CONN_USER}" style="display:block;">READY</div>
</div>
</div>
`;
$c_a_p_user.append(newUser);
}
}
}
});
};
입장한 사용자 방장, 일반 구분하여 이벤트 실행(서버)
server.js(line 390)
if (room_host != nickname) {
ArenaNamespace.to(room_number).emit("enter_normal_user", {
conn_user,
room_host,
room_number,
});
socket.emit("normal_user_ready");
} else {
ArenaNamespace.to(room_number).emit("enter_host_user", {
conn_user,
room_host,
room_number,
});
}
입장한 사용자 방장, 일반 구분하여 이벤트 실행(클라이언트)
codeArenaList.js(line 570)
arenaSocket.on("enter_host_user", ({ conn_user, room_host, room_number }) => {
const $c_a_p_user = document.querySelector(".c_a_p_user");
const $divs = $c_a_p_user.querySelectorAll("div");
$divs.forEach(($div) => {
$div.remove();
});
// userList는 전체 유저가 입장한 방번호와 닉네임을 객체로 배열에 넣은 것
// room_number는 입장하는 방의 번호
// room_host는 입장하는 방을 만든 이
updateArenaNickname(conn_user, room_host, room_number);
});
arenaSocket.on("enter_normal_user", ({ conn_user, room_host, room_number }) => {
const $c_a_p_user = document.querySelector(".c_a_p_user");
const $divs = $c_a_p_user.querySelectorAll("div");
$divs.forEach(($div) => {
$div.remove();
});
updateArenaNickname(conn_user, room_host, room_number);
});
arenaSocket.on("normal_user_ready", () => {
buttonDiv.style.display = "block";
$readyBtn.style.display = "block";
$startBtn.style.display = "none";
});
방 생성 및 입장.gif
- 방장이 게임을 시작할 수 있습니다. 하지만 방에 입장한 사용자 중 방장을 제외한 모든 사용자가 Ready를 해야 합니다.
- 방장을 제외한 모든 사용자는 입장할 때, Ready 값이 'N'인 상태로 DB에 저장됩니다.
- Ready를 클릭하면 axios를 통해 DB를 업데이트하여 해당 사용자의 Ready 값이 'Y'또는 'N'으로 바뀌게 되고,
모든 인원의 Ready 값이 'Y'일 때 방장이 Start버튼을 클릭하면 게임이 시작됩니다. - axios를 통해 Start / Ready 버튼 클릭 시, DB에 접근하여 모든 사용자의 Ready 값을 수정 및 조회 한 후, 값에 따른 스크립트가 실행됩니다.
일반 사용자 준비 완료/ 취소 기능
codeArenaList.js (line 330)
// 일반 사용자 준비 완료 / 취소 기능
// 레디 버튼을 눌렀을 때 실행되는 함수
const ready = () => {
arenaSocket.emit("click_ready_btn", { nickName: currentNickname });
};
// 클릭 이벤트 리스너 등록
$readyBtn.addEventListener("click", ready);
// DB에 접근하여 Ready 값 변경
arenaSocket.on("my_ready", (data) => {
axios.post("/codeArena/codeReady", { data }).then((res) => {
let data = JSON.parse(res.data);
arenaSocket.emit("update_ready", data[0]);
});
});
// 준비 / 준비 취소 기능
arenaSocket.on("ready_on", (data) => {
let $normal_user = document.querySelectorAll(".normal_user"); // 들어온 일반유저 닉네임 태그 전부 가져오기
let click_nickname = data.nickName; // 클릭한 사용자의 닉네임
let roomNum = data.roomNum; // 클릭한 사용자가 속해있는 방 번호
let isReady = data.isReady;
$normal_user.forEach((user_nick) => {
// user_nick : 각 일반유저의 닉네임
let foreach_nickname = user_nick.dataset.user;
if (isReady == "Y") {
// 준비X => 준비
if (click_nickname == foreach_nickname) {
// 클릭한 사용자만 활성화 하기위해
let ready_on = document.querySelector(`.${foreach_nickname}`);
ready_on.style.display = "block"; // 준비 표시 활성화
arenaSocket.emit("ready_count_up"); // 현재 준비한 인원수+ 체크
}
} else {
// 준비 => 준비X
if (click_nickname == foreach_nickname) {
// 클릭한 사용자만 활성화 하기위해
let ready_on = document.querySelector(`.${foreach_nickname}`);
ready_on.style.display = "none"; // 준비 표시 비활성화
// 여기에다가 DB USER_READY를 N로 변경
arenaSocket.emit("ready_count_down"); // 현재 준비한 인원수- 체크
}
}
});
});
Ready를 한 인원수 카운트
codeArenaList.js (line 379)
// Ready 한 인원수 카운트
let ready_count;
let currentUsers;
arenaSocket.on("user_count", (user_count) => {
currentUsers = user_count.user_count;
});
arenaSocket.on("ready_count", () => {
axios
.post("/codeArena/readyCount", { roomNum: arenaSocket.roomNum })
.then((res) => {
let arenaUsers = JSON.parse(res.data);
ready_count = arenaUsers[0].COUNT;
});
});
방장 Start 버튼 클릭 로직
codeArenaList.js (line 396)
// 방장이 start 버튼을 눌렀을 때
$startBtn.addEventListener("click", () => {
if (arenaSocket.isAdmin) {
currentUsers = parseInt(currentUsers);
ready_count = parseInt(ready_count);
if (currentUsers - 1 == ready_count) {
arenaSocket.emit("click_start_btn");
$startBtn.style.display = "none";
buttonDiv.style.display = "none";
TIMER();
question_div.style.display = "block";
question_div2.style.display = "block";
}
else { // 시작 조건 미충족일 때 클릭 시 start 빨간색 => 하얀색
$startBtn.classList.add("clicked"); // clicked 클래스 추가
setTimeout(() => {
$startBtn.classList.remove("clicked")
}, 500)
}
}
//code editor 기본 값 입력
js.setValue(`function codeBuddy(n){
let result;
result = 정답을입력하세요;
return result;
}`);
});
//방장이 Game 시작시 모든유저 ready Y -> N 변경
arenaSocket.on('gameStart',(roomNum)=>{
axios.post('/codeArena/gameStart', {roomNum})
})
// 방장이 Game 시작시 모든유저 ready 표시 해제
arenaSocket.on("remove_ok", () => {
let green_ok = document.querySelectorAll(".u_r_circle")
green_ok.forEach((ok) => {
ok.style.display = "none"
})
})
Start Game.gif
- 주어진 문제에 대해 알고리즘 로직을 작성하고, 코드 실행을 클릭하면 실행 결과가 출력 됩니다.
- 코드 실행 클릭 시, axios로 '문제 번호'에 대한 입력값과 출력값(정답)을 DB에서 응답 받습니다.
- 그리고 Code Editor에 작성한 알고리즘(value) 값을 가져와서 입력 값을 대입해 실행시킨 결과 값과, 출력 값을 검증해서
- 정답 유무를 판단하여 iframe에 출력해줍니다.
- 코드 제출 클릭 시, 코드 실행과 같이 작성한 알고리즘을 실행시켜 검증 후, 정답이라면 ok 아이콘이 활성화 됩니다.
- 방 인원이 모두 ok 아이콘이 활성화 되었을 때, 게임이 종료됩니다.
코드 실행 버튼 클릭이벤트
codeArena.js (line 751)
// 코드실행 버튼 클릭 이벤트
codeStart.addEventListener('click', async(e)=>{
e.preventDefault()
// codeStart 요청을 보내서 문제번호와 일치하는 입력값과 출력값 database에서 반환
const res = await axios.post('/codeArena/codeStart', {jsValue})
.then(res=>{
let data = JSON.parse(res.data)
//입력값
let resultInput = data[0].RESULT_INPUT
//출력값
let resultOutput = data[0].RESULT_OUTPUT
// script 태그 생성
const scriptElement = document.createElement("script");
// 생성한 script 태그안에 사용자가 입력한 값 추가
scriptElement.innerHTML = `var n = ${resultInput};
var outPut = ${resultOutput};
${jsValue};
try{
console.log('코드실행중');
if(typeof codeBuddy !== 'undefined'){
if(codeBuddy(n) == outPut){
let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
document.body.innerHTML = '정답입니다.<br>'+data
}
else{
let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
document.body.innerHTML = '틀렸습니다.<br>'+data
}
}
else {
throw new Error('codeBuddy 함수가 정의되지 않았습니다.');
}
}
catch(err){
console.error('에러발생',err);
var body = document.body
body.innerHTML = err
body.style.color = 'red'
}
`
// script를 body 태그 안에 추가(script가 정상작동하게 하기위함)
outPut.contentWindow.document.body.appendChild(scriptElement);
})
})
코드 제출 버튼 클릭이벤트
codeArenaList.js (line 805)
// 코드제출 버튼 클릭 이벤트
codeSubmit.addEventListener('click', async(e)=>{
e.preventDefault()
// codeStart 요청을 보내서 문제번호와 일치하는 입력값과 출력값 database에서 반환
const res = await axios.post('/codeArena/codeStart', {jsValue})
.then(res=>{
let data = JSON.parse(res.data)
//입력값
let resultInput = data[0].RESULT_INPUT
//출력값
let resultOutput = data[0].RESULT_OUTPUT
// script 태그 생성
const scriptElement = document.createElement("script");
// 생성한 script 태그안에 사용자가 입력한 값 추가
scriptElement.innerHTML = `
var n = ${resultInput};
var outPut1 = ${resultOutput};
var name = '${currentNickname}';
var startResult;
${jsValue};
try{
if(typeof codeBuddy !== 'undefined'){
if(codeBuddy(n) == outPut1){
let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
let userS = document.querySelectorAll('.u_i_nick')
userS.forEach((user)=>{
if(name == user.dataset.user){
arenaSocket.emit('please', name)
arenaSocket.emit('pleaesRoomNum')
}
})
}
else{
let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
}
}
else {
throw new Error('codeBuddy 함수가 정의되지 않았습니다.');
}
}
catch(err){
console.error('에러발생',err);
}
`
// script를 body 태그 안에 추가(script가 정상작동하게 하기위함)
document.body.appendChild(scriptElement);
})
})
문제 정답 시 실행로직
codeArenaList.js (line 862)
// 정답 제출한 사용자 ok 아이콘 활성화
arenaSocket.on('testSucess',(data)=>{
let name = data
let check = document.querySelector(`.${data}`)
check.style.display = 'block'
check.innerText = 'ok'
})
// 전체 사용자 모두 정답시 게임 종료
arenaSocket.on('okRoomNum',(data)=>{
let data1 = data.roomNum
axios.post('/codeArena/testSucess',{roomNum:data1, name:currentNickname})
.then(res=>{
let data = JSON.parse(res.data)
data = data.COUNT
if(data == currentUsers){
arenaSocket.emit('gameSet')
}
})
})