[큐빙봇] 파일 쓰기 없이 작동하는 아이피 탐지
분류
팁
조회 수
6,669
추천 수
0
이슈:
- 한 서버 아이피에 2봇 이상 큐빙할 경우 튕길 가능성이 높다.
- 파일 읽기/쓰기가 겹칠 경우 때때로 D2Bot 튕긴다. - 이 경우는 OS 재시작해야 함
해결 방안:
- duper의 아이피 주소로 같은 gameserverip 접속할 시 3~4회만 큐빙하고 다음방으로 넘어간다.
- 파일 액세스를 하지 않는다.
한계:
- 2개 이상 D2Bot 실행 한 경우 각 D2Bot 에 딸린 game.exe 끼리 아이피 중복탐지를 시행한다.
단, 실행되는 D2Bot 마다 같은 아이피 끼리 묶어주면 된다.
파일로 처리하고 쓰기폴더를 junction 묶을 수 있겠지만 파일쓰기는 D2Bot 불안정하게 만드므로 지양한다. - game1.exe game2.exe 이와 같은 클라이언트 파일명이 있는 변수를 찾을 수 없어 직접 코드내에서 설정한다.
그 외의 장점:
d2bs의 기본 캐시 메소드인 store,retrieve,remove 삼종세트는 매우 빈약한 기능을 지원해서 응용범위도 매우 제한적이다.
작성한 cacheHandler 는 다른 profile 의 데이터를 관리할 수 있고 데이터 키 값을 가질 수 있다.
참고사항:
- profile 값은 실제 profile 이 아니라 cache key 개념이다. 다만 편의상 profile 값을 쓴다.
- D2Bot.exe 종료할 때 까지 캐시는 자동으로 지워지지 않으므로 무분별하게 사용하지 않는다.
cacheHandler 소스코드 (초반부 적당한 곳에 위치)
//cachehandler.put(); .delete .get
// 파일읽기/쓰기 없이 다른 프로필과 안정적이고 빠른 연동이 가능하다.
var cacheHandler = {
_init: false,
init: function()
{
if(this._init===true) return;
this._init = true;
var thisObj = this;
addEventListener('copydata', function(mode,msg){
//print('mode: ' + mode + ' / msg: ' + msg);
if(mode!=61732) return;
var cache_value = null,
params = {}, args;
if(typeof(msg)=='string' && msg.match(/^cachehandler\:/))
params = JSON.parse(msg.replace(/^cachehandler\:/, ''));
// args[0] = cachekey, args[1]=callback_func, args[2] = profile
args = thisObj.callback_args.shift();
// cache value is undefined
if(typeof(params[args[0]])!='string') params[args[0]] = null;
// profile must be same
/*if(typeof(params.profile)!='string') params.profile = args[2];
if(args[2]==params.profile)*/
args[1]( params[args[0]], params );
if(thisObj.callback_args.length)
{
sendCopyData(null, D2Bot.handle, 0, JSON.stringify( thisObj.callback_args[0][3] ));
}
});
},
put:function(cache_key, cache_value, profile)
{
if(typeof(profile)!='string' || !profile.length) profile = me.profile;
this.init();
var thisObj = this;
this.get(cache_key, function(dummy, params){
params[cache_key] = cache_value;
thisObj._erase(profile);
thisObj._write(profile, params);
}, profile);
},
delete: function(cache_key, profile)
{
if(typeof(profile)!='string' || !profile.length) profile = me.profile;
this.init();
var thisObj = this;
this.get(cache_key, function(dummy, params){
delete(params[cache_key]);
var i, ii = 0;
for(i in params)
{
if(!params.hasOwnProperty(i)) continue;
ii++;
}
if(!ii) thisObj._erase(profile);
else thisObj._write(profile, params);
}, profile);
},
get: function(cache_key, callback_func, profile)
{
if(typeof(profile)!='string' || !profile.length) profile = me.profile;
this.init();
var obj = {
profile: me.profile,
func: 'retrieve',
args: [profile]
};
this.callback_args.push([cache_key, callback_func, profile, obj]);
if(this.callback_args.length==1)
sendCopyData(null, D2Bot.handle, 0, JSON.stringify(obj));
},
_write: function(profile, params)
{
var obj = {
profile: me.profile,
func: "store",
args: [profile, 'cachehandler:'+JSON.stringify(params)]
};
sendCopyData(null, D2Bot.handle, 0, JSON.stringify(obj));
},
_erase: function(profile)
{
sendCopyData(null, D2Bot.handle, 0, JSON.stringify({
profile: me.profile,
func: "delete",
args: [profile]
}));
},
callback_args: []
};
// 파일읽기/쓰기 없이 다른 프로필과 안정적이고 빠른 연동이 가능하다.
var cacheHandler = {
_init: false,
init: function()
{
if(this._init===true) return;
this._init = true;
var thisObj = this;
addEventListener('copydata', function(mode,msg){
//print('mode: ' + mode + ' / msg: ' + msg);
if(mode!=61732) return;
var cache_value = null,
params = {}, args;
if(typeof(msg)=='string' && msg.match(/^cachehandler\:/))
params = JSON.parse(msg.replace(/^cachehandler\:/, ''));
// args[0] = cachekey, args[1]=callback_func, args[2] = profile
args = thisObj.callback_args.shift();
// cache value is undefined
if(typeof(params[args[0]])!='string') params[args[0]] = null;
// profile must be same
/*if(typeof(params.profile)!='string') params.profile = args[2];
if(args[2]==params.profile)*/
args[1]( params[args[0]], params );
if(thisObj.callback_args.length)
{
sendCopyData(null, D2Bot.handle, 0, JSON.stringify( thisObj.callback_args[0][3] ));
}
});
},
put:function(cache_key, cache_value, profile)
{
if(typeof(profile)!='string' || !profile.length) profile = me.profile;
this.init();
var thisObj = this;
this.get(cache_key, function(dummy, params){
params[cache_key] = cache_value;
thisObj._erase(profile);
thisObj._write(profile, params);
}, profile);
},
delete: function(cache_key, profile)
{
if(typeof(profile)!='string' || !profile.length) profile = me.profile;
this.init();
var thisObj = this;
this.get(cache_key, function(dummy, params){
delete(params[cache_key]);
var i, ii = 0;
for(i in params)
{
if(!params.hasOwnProperty(i)) continue;
ii++;
}
if(!ii) thisObj._erase(profile);
else thisObj._write(profile, params);
}, profile);
},
get: function(cache_key, callback_func, profile)
{
if(typeof(profile)!='string' || !profile.length) profile = me.profile;
this.init();
var obj = {
profile: me.profile,
func: 'retrieve',
args: [profile]
};
this.callback_args.push([cache_key, callback_func, profile, obj]);
if(this.callback_args.length==1)
sendCopyData(null, D2Bot.handle, 0, JSON.stringify(obj));
},
_write: function(profile, params)
{
var obj = {
profile: me.profile,
func: "store",
args: [profile, 'cachehandler:'+JSON.stringify(params)]
};
sendCopyData(null, D2Bot.handle, 0, JSON.stringify(obj));
},
_erase: function(profile)
{
sendCopyData(null, D2Bot.handle, 0, JSON.stringify({
profile: me.profile,
func: "delete",
args: [profile]
}));
},
callback_args: []
};
cacheHandler 이용하여 중복 아이피 탐지
/*
index = me.profile.replace(/[^\d]/gi, ''), // 팀 번호
max_dupe_cycle = 50 + Math.round(Math.random()*20),
duperProxyGroup = { //
'2': [1,2,3,4,5,6], // Duper1,Duper2,Duper3,Duper4,Duper5,
'3': [7,8,9,10,11,12],
'4': [13,14,15,16,17,18],
'5': [19,20,21,22,23,24],
'6': [25,26,27,28,29,30,37],
'8': [31,32,33,34,35,36,38]
},
*/
if(me.profile.match(/Duper/i)) (function(){
cacheHandler.put('ipaddress', me.gameserverip);
// 아이피 체크하고 중뷁이면 4번만 큐빙하고 나감.
var ii, proxy_key = null, proxy_group = [];
for(var i in duperProxyGroup)
{
if(!duperProxyGroup.hasOwnProperty(i)) continue;
if(duperProxyGroup[i].indexOf( Number(index) ) >= 0)
{
proxy_key = i.toString();
proxy_group = duperProxyGroup[i];
break;
}
}
for(i=0; i<proxy_group.length; i++)(function(){
// 자기자신은 패스.
if(proxy_group[i]==Number(index)) return;
var profile = 'Duper' + proxy_group[i].toString();
cacheHandler.get('ipaddress', function(cache_value){
// null 값이면 패스.
if(typeof(cache_value)!='string') return;
print(profile + "'s ipaddress: " + cache_value);
// duper 아이피 중복되는 경우
if(me.gameserverip==cache_value)
{
if(max_dupe_cycle > 3) max_dupe_cycle = 3;
else max_dupe_cycle--; // 값이 음수가 되어도 큐빙 1회는 한다.
print('ipaddress has been duplicated, max_dupe_cycle value will be ' + max_dupe_cycle.toString());
}
}, profile);
})();
})();
index = me.profile.replace(/[^\d]/gi, ''), // 팀 번호
max_dupe_cycle = 50 + Math.round(Math.random()*20),
duperProxyGroup = { //
'2': [1,2,3,4,5,6], // Duper1,Duper2,Duper3,Duper4,Duper5,
'3': [7,8,9,10,11,12],
'4': [13,14,15,16,17,18],
'5': [19,20,21,22,23,24],
'6': [25,26,27,28,29,30,37],
'8': [31,32,33,34,35,36,38]
},
*/
if(me.profile.match(/Duper/i)) (function(){
cacheHandler.put('ipaddress', me.gameserverip);
// 아이피 체크하고 중뷁이면 4번만 큐빙하고 나감.
var ii, proxy_key = null, proxy_group = [];
for(var i in duperProxyGroup)
{
if(!duperProxyGroup.hasOwnProperty(i)) continue;
if(duperProxyGroup[i].indexOf( Number(index) ) >= 0)
{
proxy_key = i.toString();
proxy_group = duperProxyGroup[i];
break;
}
}
for(i=0; i<proxy_group.length; i++)(function(){
// 자기자신은 패스.
if(proxy_group[i]==Number(index)) return;
var profile = 'Duper' + proxy_group[i].toString();
cacheHandler.get('ipaddress', function(cache_value){
// null 값이면 패스.
if(typeof(cache_value)!='string') return;
print(profile + "'s ipaddress: " + cache_value);
// duper 아이피 중복되는 경우
if(me.gameserverip==cache_value)
{
if(max_dupe_cycle > 3) max_dupe_cycle = 3;
else max_dupe_cycle--; // 값이 음수가 되어도 큐빙 1회는 한다.
print('ipaddress has been duplicated, max_dupe_cycle value will be ' + max_dupe_cycle.toString());
}
}, profile);
})();
})();
cachehandler 테스트 예제 (개발할 것 아니면 볼 필요 없음)
cacheHandler.put('ipaddress', me.gameserverip);
var profile_list = ['PMR','EEF','apv','Pook','KEB','Opteron'];
for(var profile_key=0; profile_key<profile_list.length; profile_key++)(function(){
// 자기자신은 패스.
if(profile_list[profile_key]==me.profile) return;
// 기본적으로 비동기.
var ip = 'que',
tick_limit = getTickCount() + 5000, // 응답시간을 5초만 준다.
profile = profile_list[profile_key];
cacheHandler.get('ipaddress', function(cache_value){
print(profile + ' / ' + cache_value);
// 값이 있으면 string, 없으면 null, object 데이터를 쓰려면 JSON.parse/
ip = cache_value;
}, profile);
// 동기화하려면 while 이용한다.
//while(ip==='que' && tick_limit > getTickCount()) delay(100);
})();
cacheHandler.put('key_xxx' , 'value_xxx' , 'test_profile');
cacheHandler.put('key_xxx2', 'value_xxx2', 'test_profile');
delay(500);
cacheHandler.get('key_xxx', function(cache_value, params){
print(cache_value);
}, 'key_xxx');
delay(500);
cacheHandler.delete('key_xxx', 'test_profile');
delay(500);
cacheHandler.get('key_xxx', function(cache_value, params){
print( 'key_xxx: ');
print( params===null ); // false
print( cache_value===null ); // true
}, 'test_profile');
cacheHandler.get('key_xxx2', function(cache_value, params){
print( 'key_xxx2: ');
print( params===null ); // false
print( cache_value ); // value_xxx2
}, 'test_profile');
var profile_list = ['PMR','EEF','apv','Pook','KEB','Opteron'];
for(var profile_key=0; profile_key<profile_list.length; profile_key++)(function(){
// 자기자신은 패스.
if(profile_list[profile_key]==me.profile) return;
// 기본적으로 비동기.
var ip = 'que',
tick_limit = getTickCount() + 5000, // 응답시간을 5초만 준다.
profile = profile_list[profile_key];
cacheHandler.get('ipaddress', function(cache_value){
print(profile + ' / ' + cache_value);
// 값이 있으면 string, 없으면 null, object 데이터를 쓰려면 JSON.parse/
ip = cache_value;
}, profile);
// 동기화하려면 while 이용한다.
//while(ip==='que' && tick_limit > getTickCount()) delay(100);
})();
cacheHandler.put('key_xxx' , 'value_xxx' , 'test_profile');
cacheHandler.put('key_xxx2', 'value_xxx2', 'test_profile');
delay(500);
cacheHandler.get('key_xxx', function(cache_value, params){
print(cache_value);
}, 'key_xxx');
delay(500);
cacheHandler.delete('key_xxx', 'test_profile');
delay(500);
cacheHandler.get('key_xxx', function(cache_value, params){
print( 'key_xxx: ');
print( params===null ); // false
print( cache_value===null ); // true
}, 'test_profile');
cacheHandler.get('key_xxx2', function(cache_value, params){
print( 'key_xxx2: ');
print( params===null ); // false
print( cache_value ); // value_xxx2
}, 'test_profile');
cacheHandler 적용시 프로필과 프로필간 커뮤니케이션에
- 데이터를 읽고 쓰며 통신하는 방법이나
- 두 캐릭이 대화를 하여 통신하는 방법보다
더 빠르고, 더 가볍고(high performance), 더 안정적(=less error)이다.

ㅜㅜ 저 하나 여쭈어 봐도 될까요 ...