가위바위보 플러그인 예제
개요
- 유저메뉴에 가위바위보 신청을 등록합니다.
- 상태 패턴을 이용하여 작성되었습니다.
- 해시를 이용하여 상대의 가위바위보를 검증합니다.
- 타이머를 이용하여 지정시간이후에는 다시 원상태로 돌아오게 되어있습니다.
사용된 API
소스
<script> // 가위바위보 플러그인
U.chat('*').on('after.create#RPS', function(room, data) {
/*
* after.create 때 작동하는 콜백입니다.
* 이 콜백의 아이디는 #RPS 입니다.
*/
var channel = room.plugin.add('RPS'); // RPS 이름의 채널를 가져옵니다.
var randomKey, myChoice, target, targetHash, status, wait_timer, response_timer, popup_id; // 기초정보를 정의해줍니다.
var states = { // 상태 패턴을 이용하여 작성하였습니다.
'normal' : {
'would' : function( room, data) { // 누군가가 나에게 가위바위보를 요청했을때 수신하게되는 함수입니다.
target = data.user.nick; // 가위바위보의 상대방을 정의하는 부분입니다.
popup_id = room.skin.popup.confirm('가위바위보신청', data.user.nick+' 님이 가위바위보를 신청하였습니다.', function(result) { // room.skin.popup 를 이용하여 가위바위보를 물어보는 함수입니다.
if(result) { // 승낙한 경우입니다.
channel.sendTo(data.user.nick, ['apply']); // 상대에게 승낙을 알힙니다.
status = states.wait_choice; // 상태를 '상대의 가위바위보 결정을 대기' 로 바꿉니다.
selectRPS(); // 가위바위보 선택창을 띄웁니다.
} else {
channel.sendTo(data.user.nick, ['reject']);
init(); // 거절하거나, 무시하면 모든 변수를 초기화합니다.
}
});
response_timer_start();
}
},
'wait_apply' : {
'apply' : function(room, data) { // 내가 보낸 요청을, 상대방이 수락했을때 입니다.
if(data.user.nick != target) return false; // 내가 요청한 인물이였는지를 확인합니다.
status = states.wait_choice; // '상대방의 가위바위보 결정을 대기'하는 상태로 바꿉니다.
selectRPS(); // 가위바위보 선택창을 띄웁니다.
},
'reject' : function(room, data){ // 거절되면 select 팝업레이어를 지웁니다.
if(data.user.nick != target) return false; // 상대방이 나에게 요청을 수락했는지 그리고 내가 요청한 인물이였는지를 확인합니다.
room.print('상대방이 거절했거나, 반응이 없습니다.');
room.skin.popup.close(popup_id);
init(); // 초기화시킵니다.
}
},
'wait_choice' : {
'hash' : function(room, data) { // 상대방이 값을 결정하여, 인증해시를 보내준 경우입니다.
if(data.user.nick != target) return false; // 내가 요청한 인물이였는지를 확인합니다.
targetHash = data.data[1]; // 상대의 인증해시를 저장합니다.
if ( myChoice != -1) { // 내가 결정을 안한 상태라면, 결과를 만들수 없고, 보낼 값도 없으므로 넘어갑니다.
channel.sendTo(target, ['myChoice'].concat(randomKey, myChoice)); // 나의 결정을 보냅니다.
status = states.wait_result; // 결과를 기다리는 상태로 변경합니다.
}
},
'reject' : function(room, data){ // 거절되면 confirm 팝업레이어를 지웁니다.
if(data.user.nick != target) return false; // 상대방이 나에게 요청을 수락했는지 그리고 내가 요청한 인물이였는지를 확인합니다.
room.print('상대방이 거절했거나, 반응이 없습니다.');
room.skin.popup.close(popup_id);
init(); // 초기화시킵니다.
}
},
'wait_result' : {
'myChoice' : function(room, data) { // 상대방이 자신의 손을 보여줍니다.
if(data.user.nick != target) return false; // 내가 요청한 인물이였는지를 확인합니다.
if(makeHash([data.data[1], data.data[2], data.data[3], data.data[4]], data.data[5]) == targetHash) { // 'hash' 부분에서 왔던 인증해시 와 들어온 데이터로 만든 인증해시를 비교합니다.
room.print('나 : '+n2s(myChoice)+', 상대방 : '+n2s(data.data[5])); // 값을 표시합니다.
room.skin.popup.alert('가위바위보 결과', jud(myChoice, data.data[5])); // 결과를 room.skin.popup 을 이용하여 사용자에게 팝업레이어를 띄웁니다.
} else {
alert('해쉬 오류'); // 어떠한 이유에서인지 상대방이 보낸 인증해시와 그후에 보낸 값이 일치하지 않습니다.
// 이경우에는 상대방이 값을 조작했을 경우입니다.
}
init(); // 결과가 끝났으므로 재정의해줍니다.
},
'reject' : function(room, data){ // 거절되면 confirm 팝업레이어를 지웁니다.
if(data.user.nick != target) return false; // 상대방이 나에게 요청을 수락했는지 그리고 내가 요청한 인물이였는지를 확인합니다.
room.print('상대방이 거절했거나, 반응이 없습니다.'); // 안내 메세지를 띄웁니다
room.skin.popup.close(popup_id);
init(); // 초기화시킵니다.
}
},
}
init(); // 모든 변수를 초기화해줍니다.
room.skin.userMenu.add({ // room.skin.userMenu 를 이용하여 유저를 눌렀을때 가위바위보가 뜨게 합니다.
id:'RPS' // 아이디입니다.
, text:'가위바위보신청' // 유저메뉴에 글자입니다.
, onClick: function(room, data) { // 콜백입니다.
if(status !== states.normal) {
room.print('이미 게임이 진행중입니다.');
return false;
}
status = states.wait_apply; // 신청하는 순간, 상태를 wait_apply 로 변경합니다.
target = data.target; // 타겟을 설정하고
channel.sendTo(data.target, ['would']); // 채널에 전송합니다.
wait_timer_start();
}
});
channel.onReceived(function(room, data) { // 채널리스너 입니다. RPS 채널로 오는 모든 데이터를 듣습니다.
if(data.type != room.plugin.ONLY_ME) return false; // 나에게만 보낸 메세지가 아니므로 무시합니다.
status[data.data[0]] && status[data.data[0]](room, data); // 상태 패턴을 이용하기떄문에 현재 상태함수에 값을 넘겨줍니다.
});
room.on('after.join#RPS', function(room, data) { // 유저가 채팅방에 접속하면 플러그인이 작동한다는 사실을 알려줍니다.
room.print('가위바위보 플러그인 작동');
});
function response_timer_start() { // 나의 반응에 제한을 겁니다.
stop_timer(); // 모든 타이머는 하나만 돌아가고 있으면 됩니다 !
response_timer = setTimeout(function() { // 타이머를 정의합니다.
if(target) { // 상대가 정해져있다면 상대에게 거절의사를 보냅니다.
channel.sendTo(target, ['reject']);
}
init(); // 게임을 초기화합니다.
}, 15000);
}
function wait_timer_start() { // 요청, 가위바위보결정 등에 제한시간을 걸어 오랜시간 결정을 안하는지 판단합니다.
stop_timer(); // 타이머를 초기화합니다.
wait_timer = setTimeout(function() { // 타이머를 정의합니다.
room.print('상대가 거절했거나, 반응이 없습니다.'); // 안내 메세지를 띄웁니다 .
init(); // 게임을 초기화합니다.
}, 15000);
}
function stop_timer() { // 게임타이머을 초기화합니다.
if(wait_timer)
clearTimeout(wait_timer);
if(response_timer)
clearTimeout(response_timer);
wait_timer = response_timer = undefined;
}
function init() { // 변수를 초기화해주는 함수입니다.
randomKey = []; // 인증을 위한 랜덤값을 저장하는 배열입니다.
myChoice = -1; // 내가 결정한 값을 저장해둡니다.
target = undefined; // 상대방을 저장해둡니다. ( 닉네임 기반 )
targetHash = ''; // 상대방이 보낸 인증해시를 저장하는 변수입니다.
status = states.normal; // 상태를 보통상태(아무것도없는상태)로 돌립니다.
room.skin.popup.close(popup_id); // popup 을 끕니다.
popup_id = 0; // 팝업 아이디를 초기화합니다.
stop_timer();
}
function selectRPS() { // 가위 바위 보 를 결정하는 팝업레이어를 띄우는 기능을하는 함수입니다.
if( status != states.wait_choice ) return false;
popup_id = room.skin.popup.select('가위바위보', '선택하기', {0:'가위', 1:'바위', 2:'보'}, function(result) { // room.skin.popup 모듈을 이용하여 select 이 담긴 팝업레이어를 띄웁니다.
if(result == 0 || result == 1 || result == 2) { // 선택한 사항이 있는경우만 해당
randomKey = [randomString(4), randomString(4), randomString(4), randomString(4)]; // 내가 결정한 값을 인증하기위한 인증해시의 salt, 랜덤키를 만듭니다.
channel.sendTo(target, ['hash', makeHash(randomKey, result)]); // 대상자에게 인증해시만를 보냅니다.
wait_timer_start();
if(targetHash) { // 이미 상대방의 인증해시가 들어온경우 상대방에게 내가 결정한 값을 알려줍니다.
channel.sendTo(target, ['myChoice'].concat(randomKey, result)); // 랜덤값 4개와 값을 보냅니다.
status = states.wait_result; // 결과를 기다리는 상태로 변경합니다.
}
myChoice = result; // 내 결과를 변수에 저장합니다.
} else {
selectRPS();
}
});
response_timer_start();
}
function n2s(choice) { // 숫자로 되어있는 값을 한국어로 변경해주는 함수입니다.
if(choice == 0)
return '가위';
if(choice == 1)
return '바위';
if(choice == 2)
return '보';
}
function jud(a, b) { // 가위바위보 승패를 판단하는 부분입니다.
if(a == b)
return 'draw';
else
return (a+1)%3 == b? 'lose': 'win';
}
function hashCode( str ) { // 글자를 특유의 hash 로 변경해주는 함수입니다.
var hash = 0;
if (str.length == 0) return hash;
for (i = 0; i < str.length; i++) {
char = str.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
function randomString(string_length) { // 랜덤 string 을 return 해주는 함수입니다.
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var randomstring = '';
for (var i=0; i<string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
return randomstring;
}
function makeHash(rn, choice) { // 플러그인개발자가 정한, 임의의 해시 제조법 입니다.
return hashCode(rn.join(choice))+''; // hashCode( [랜덤값 4개 ].join(가위바위보) ) 입니다.
}
});
</script>