فهرست منبع

New macro detection features (#7359)

* Adds battle config macro_detection_punishment to ban or jail characters.
* Adds battle config macro_detection_punishment_time to adjust the duration of ban/jail for players who fail the captcha challenges.
* Adds script command macro_detector which invokes the captcha challenge.
* General cleanups to the jail functions to remove duplicate code.
* General cleanups to the macro punishment calls to remove duplicate code.
* Ending SC_JAILED will now properly remove the jail status rather than trying to reset the timer to 1 second resolving any possibility of players getting stuck.
Thanks to @Lemongrass3110!
Aleos 2 سال پیش
والد
کامیت
ee2dcf816e
10فایلهای تغییر یافته به همراه154 افزوده شده و 55 حذف شده
  1. 11 0
      conf/battle/client.conf
  2. 22 0
      doc/script_commands.txt
  3. 6 32
      src/map/atcommand.cpp
  4. 2 0
      src/map/battle.cpp
  5. 2 0
      src/map/battle.hpp
  6. 1 1
      src/map/clif.cpp
  7. 85 17
      src/map/pc.cpp
  8. 4 1
      src/map/pc.hpp
  9. 21 0
      src/map/script.cpp
  10. 0 4
      src/map/status.cpp

+ 11 - 0
conf/battle/client.conf

@@ -158,3 +158,14 @@ macro_detection_retry: 3
 // Amount of time in milliseconds before the macro detection will fail and the user will be banned.
 // Official: 60000
 macro_detection_timeout: 60000
+
+// Macro Detector punishment type
+// 0 - Ban
+// 1 - Jail
+// Official: 0
+macro_detection_punishment: 0
+
+// Macro Detector punishment duration
+// Amount of time in minutes that the punishment type is active for. Use 0 for infinite.
+// Official: 0
+macro_detection_punishment_time: 0

+ 22 - 0
doc/script_commands.txt

@@ -6579,6 +6579,28 @@ Examples:
 
 ---------------------------------------
 
+macro_detector({<account ID>});
+macro_detector({"<character name>"});
+
+This command will display the captcha UI challenge onto the invoking character or the given <account ID>/<character name>.
+
+Example:
+	// Use 'getareaunits' to gather an area of players to test.
+	// Build an int array of the account IDs.
+	.@num = getareaunits(BL_PC, "prontera", 150, 150, 160, 160, .@array[0]);
+
+	mes "The number of Players in Prontera in between 150x150 and 160x160 is " + .@num + " .";
+	mes "Players to challenge:";
+	freeloop(1); // If the list is too big
+	for(.@i = 0; .@i < getarraysize(.@array); .@i++) {
+		mes (.@i + 1) + " " + convertpcinfo(.@array[.@i], CPC_NAME);
+		macro_detector .@array[.@i];
+	}
+	freeloop(0);
+	end;
+
+---------------------------------------
+
 ==================================
 |5.- Mob / NPC -related commands.|
 ==================================

+ 6 - 32
src/map/atcommand.cpp

@@ -5218,8 +5218,7 @@ ACMD_FUNC(servertime)
 ACMD_FUNC(jail)
 {
 	struct map_session_data *pl_sd;
-	int x, y;
-	unsigned short m_index;
+
 	nullpo_retr(-1, sd);
 
 	memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
@@ -5244,21 +5243,7 @@ ACMD_FUNC(jail)
 		return -1;
 	}
 
-	switch(rnd() % 2) { //Jail Locations
-	case 0:
-		m_index = mapindex_name2id(MAP_JAIL);
-		x = 24;
-		y = 75;
-		break;
-	default:
-		m_index = mapindex_name2id(MAP_JAIL);
-		x = 49;
-		y = 75;
-		break;
-	}
-
-	//Duration of INT_MAX to specify infinity.
-	sc_start4(NULL,&pl_sd->bl,SC_JAILED,100,INT_MAX,m_index,x,y,1000);
+	pc_jail(*pl_sd);
 	clif_displaymessage(pl_sd->fd, msg_txt(sd,117)); // GM has send you in jails.
 	clif_displaymessage(fd, msg_txt(sd,118)); // Player warped in jails.
 	return 0;
@@ -5296,7 +5281,7 @@ ACMD_FUNC(unjail)
 	}
 
 	//Reset jail time to 1 sec.
-	sc_start(NULL,&pl_sd->bl,SC_JAILED,100,1,1000);
+	pc_jail(*pl_sd, 0);
 	clif_displaymessage(pl_sd->fd, msg_txt(sd,120)); // A GM has discharged you from jail.
 	clif_displaymessage(fd, msg_txt(sd,121)); // Player unjailed.
 	return 0;
@@ -5305,8 +5290,8 @@ ACMD_FUNC(unjail)
 ACMD_FUNC(jailfor) {
 	struct map_session_data *pl_sd = NULL;
 	char * modif_p;
-	int jailtime = 0,x,y;
-	short m_index = 0;
+	int jailtime = 0;
+
 	nullpo_retr(-1, sd);
 
 	memset(atcmd_output, '\0', sizeof(atcmd_output));
@@ -5363,19 +5348,8 @@ ACMD_FUNC(jailfor) {
 		return -1;
 	}
 
-	// Jail locations, add more as you wish.
-	switch(rnd()%2) {
-		case 1: // Jail #1
-			m_index = mapindex_name2id(MAP_JAIL);
-			x = 49; y = 75;
-			break;
-		default: // Default Jail
-			m_index = mapindex_name2id(MAP_JAIL);
-			x = 24; y = 75;
-			break;
-	}
+	pc_jail(*pl_sd, jailtime);
 
-	sc_start4(NULL,&pl_sd->bl,SC_JAILED,100,jailtime,m_index,x,y,jailtime?60000:1000); //jailtime = 0: Time was reset to 0. Wait 1 second to warp player out (since it's done in status_change_timer).
 	return 0;
 }
 

+ 2 - 0
src/map/battle.cpp

@@ -10271,6 +10271,8 @@ static const struct _battle_data {
 	{ "break_mob_equip",                    &battle_config.break_mob_equip,                 0,      0,      1,              },
 	{ "macro_detection_retry",              &battle_config.macro_detection_retry,           3,      1,      INT_MAX,        },
 	{ "macro_detection_timeout",            &battle_config.macro_detection_timeout,         60000,  0,      INT_MAX,        },
+	{ "macro_detection_punishment",         &battle_config.macro_detection_punishment,      0,      0,      1,              },
+	{ "macro_detection_punishment_time",    &battle_config.macro_detection_punishment_time, 0,      0,      INT_MAX,        },
 
 	{ "feature.dynamicnpc_timeout",         &battle_config.feature_dynamicnpc_timeout,      1000,   60000,  INT_MAX,        },
 	{ "feature.dynamicnpc_rangex",          &battle_config.feature_dynamicnpc_rangex,       2,      0,      INT_MAX,        },

+ 2 - 0
src/map/battle.hpp

@@ -714,6 +714,8 @@ struct Battle_Config
 	int break_mob_equip;
 	int macro_detection_retry;
 	int macro_detection_timeout;
+	int macro_detection_punishment;
+	int macro_detection_punishment_time;
 
 	int feature_dynamicnpc_timeout;
 	int feature_dynamicnpc_rangex;

+ 1 - 1
src/map/clif.cpp

@@ -24776,7 +24776,7 @@ void clif_parse_macro_reporter_ack(int fd, map_session_data *sd) {
 		return;
 	}
 
-	pc_macro_reporter_process(*sd, *tsd);
+	pc_macro_reporter_process(*tsd, sd->status.account_id);
 	clif_macro_reporter_status(*sd, MCR_MONITORING);
 #endif
 }

+ 85 - 17
src/map/pc.cpp

@@ -15235,6 +15235,76 @@ void pc_attendance_claim_reward( struct map_session_data* sd ){
 	clif_attendence_response( sd, attendance_counter );
 }
 
+/**
+ * Send a player to jail and determine the location to send in jail.
+ * @param sd: Player data
+ * @param duration: Duration in minutes (default INT_MAX = infinite)
+ */
+void pc_jail(map_session_data &sd, int32 duration) {
+	uint16 m_index = mapindex_name2id(MAP_JAIL);
+	int16 x, y;
+
+	switch (rnd() % 2) { // Jail Locations
+		case 0: // Jail #1
+			x = 49;
+			y = 75;
+			break;
+		default: // Default Jail
+			x = 24;
+			y = 75;
+			break;
+	}
+
+	duration = i32max(0, duration); // Can't be less than 0 seconds.
+
+	// If duration > 0 then triggered via jailfor which checks every minute.
+	// If duration == INT_MAX then triggered via jail for infinite duration.
+	// If duration == 0 then triggered via unjail and end status.
+	if (duration > 0)
+		sc_start4(nullptr, &sd.bl, SC_JAILED, 100, duration, m_index, x, y, 60000);
+	else
+		status_change_end(&sd.bl, SC_JAILED);
+}
+
+/**
+ * Determine the punishment type when failing macro checks.
+ * @param sd: Player data
+ * @param stype: Macro detection status type (for banning)
+ */
+static void pc_macro_punishment(map_session_data &sd, e_macro_detect_status stype) {
+	int32 duration = 0;
+
+	// Determine if there's a unique duration
+	if (battle_config.macro_detection_punishment_time > 0) {
+		char time[13];
+
+		safesnprintf(time, 13, "+%dnm", battle_config.macro_detection_punishment_time);
+		duration = static_cast<int32>(solve_time(time));
+	}
+
+	// Delete the timer
+	if (sd.macro_detect.timer != INVALID_TIMER)
+		delete_timer(sd.macro_detect.timer, pc_macro_detector_timeout);
+
+	// Clear the macro detect data
+	sd.macro_detect = {};
+	sd.macro_detect.timer = INVALID_TIMER;
+
+	// Unblock all actions for the player
+	sd.state.block_action &= ~PCBLOCK_ALL;
+	sd.state.block_action &= ~PCBLOCK_IMMUNE;
+
+	if (battle_config.macro_detection_punishment == 0) { // Ban
+		clif_macro_detector_status(sd, stype);
+		chrif_req_login_operation(sd.macro_detect.reporter_aid, sd.status.name, (duration == 0 ? CHRIF_OP_LOGIN_BLOCK : CHRIF_OP_LOGIN_BAN), duration, 0, 0);
+	} else { // Jail
+		// Send success to close the window without closing the client
+		clif_macro_detector_status(sd, MCD_GOOD);
+
+		pc_jail(sd, (duration == 0 ? INT_MAX : duration / 60));
+	}
+}
+
 /**
  * Save a captcha image to memory via /macro_register.
  * @param sd: Player data
@@ -15320,9 +15390,8 @@ TIMER_FUNC(pc_macro_detector_timeout) {
 	sd->macro_detect.retry -= 1;
 
 	if (sd->macro_detect.retry == 0) {
-		// All attempts have been exhausted block the user
-		clif_macro_detector_status(*sd, MCD_TIMEOUT);
-		chrif_req_login_operation(sd->macro_detect.reporter_aid, sd->status.name, CHRIF_OP_LOGIN_BLOCK, 0, 0, 0);
+		// All attempts have been exhausted, punish the user
+		pc_macro_punishment(*sd, MCD_TIMEOUT);
 	} else {
 		// Update the client
 		clif_macro_detector_request_show(*sd);
@@ -15373,10 +15442,9 @@ void pc_macro_detector_process_answer(map_session_data &sd, char captcha_answer[
 		// Deduct an answering attempt
 		sd.macro_detect.retry -= 1;
 
-		// All attempts have been exhausted block the user
+		// All attempts have been exhausted, punish the user
 		if (sd.macro_detect.retry <= 0) {
-			clif_macro_detector_status(sd, MCD_INCORRECT);
-			chrif_req_login_operation(sd.macro_detect.reporter_aid, sd.status.name, CHRIF_OP_LOGIN_BLOCK, 0, 0, 0);
+			pc_macro_punishment(sd, MCD_INCORRECT);
 			return;
 		}
 
@@ -15399,9 +15467,9 @@ void pc_macro_detector_disconnect(map_session_data &sd) {
 		sd.macro_detect.timer = INVALID_TIMER;
 	}
 
-	// If the player disconnects before clearing the challenge the account is banned.
+	// If the player disconnects before clearing the challenge the player is punished.
 	if (sd.macro_detect.retry != 0)
-		chrif_req_login_operation(sd.macro_detect.reporter_aid, sd.status.name, CHRIF_OP_LOGIN_BLOCK, 0, 0, 0);
+		pc_macro_punishment(sd, MCD_TIMEOUT);
 }
 
 /**
@@ -15438,10 +15506,10 @@ void pc_macro_reporter_area_select(map_session_data &sd, const int16 x, const in
 
 /**
  * Send out captcha check to player.
- * @param ssd: Source player data
- * @param tsd: Target player data
+ * @param sd: Target player data
+ * @param reporter_account_id: Account ID of reporter
  */
-void pc_macro_reporter_process(map_session_data &ssd, map_session_data &tsd) {
+void pc_macro_reporter_process(map_session_data &sd, int32 reporter_account_id) {
 	if (captcha_db.empty())
 		return;
 
@@ -15449,18 +15517,18 @@ void pc_macro_reporter_process(map_session_data &ssd, map_session_data &tsd) {
 	const std::shared_ptr<s_captcha_data> cd = captcha_db.random();
 
 	// Set macro detection data.
-	tsd.macro_detect.cd = cd;
-	tsd.macro_detect.reporter_aid = ssd.status.account_id;
-	tsd.macro_detect.retry = battle_config.macro_detection_retry;
+	sd.macro_detect.cd = cd;
+	sd.macro_detect.reporter_aid = reporter_account_id;
+	sd.macro_detect.retry = battle_config.macro_detection_retry;
 
 	// Block all actions for the target player.
-	tsd.state.block_action |= (PCBLOCK_ALL | PCBLOCK_IMMUNE);
+	sd.state.block_action |= (PCBLOCK_ALL | PCBLOCK_IMMUNE);
 
 	// Open macro detect client side.
-	clif_macro_detector_request(tsd);
+	clif_macro_detector_request(sd);
 
 	// Start the timeout timer.
-	tsd.macro_detect.timer = add_timer(gettick() + battle_config.macro_detection_timeout, pc_macro_detector_timeout, tsd.bl.id, 0);
+	sd.macro_detect.timer = add_timer(gettick() + battle_config.macro_detection_timeout, pc_macro_detector_timeout, sd.bl.id, 0);
 }
 
 /**

+ 4 - 1
src/map/pc.hpp

@@ -1722,17 +1722,20 @@ bool pc_attendance_enabled();
 int32 pc_attendance_counter( struct map_session_data* sd );
 void pc_attendance_claim_reward( struct map_session_data* sd );
 
+void pc_jail(map_session_data &sd, int32 duration = INT_MAX);
+
 // Captcha Register
 void pc_macro_captcha_register(map_session_data &sd, uint16 image_size, char captcha_answer[CAPTCHA_ANSWER_SIZE]);
 void pc_macro_captcha_register_upload(map_session_data & sd, uint16 upload_size, char *upload_data);
 
 // Macro Detector
+TIMER_FUNC(pc_macro_detector_timeout);
 void pc_macro_detector_process_answer(map_session_data &sd, char captcha_answer[CAPTCHA_ANSWER_SIZE]);
 void pc_macro_detector_disconnect(map_session_data &sd);
 
 // Macro Reporter
 void pc_macro_reporter_area_select(map_session_data &sd, const int16 x, const int16 y, const int8 radius);
-void pc_macro_reporter_process(map_session_data &ssd, map_session_data &tsd);
+void pc_macro_reporter_process(map_session_data &sd, int32 reporter_account_id = -1);
 
 #ifdef MAP_GENERATOR
 void pc_reputation_generate();

+ 21 - 0
src/map/script.cpp

@@ -26829,6 +26829,25 @@ BUILDIN_FUNC(isdead) {
 	return SCRIPT_CMD_SUCCESS;
 }
 
+BUILDIN_FUNC(macro_detector) {
+	map_session_data *sd;
+
+	if (script_hasdata(st, 2) && script_isstring(st, 2)) { // Character Name
+		if (!script_nick2sd(2, sd)) {
+			return SCRIPT_CMD_FAILURE;
+		}
+	} else { // Account ID
+		if (!script_accid2sd(2, sd)) {
+			return SCRIPT_CMD_FAILURE;
+		}
+	}
+
+	// Reporter Account ID as -1 for server.
+	pc_macro_reporter_process(*sd);
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
 #include "../custom/script.inc"
 
 // declarations that were supposed to be exported from npc_chat.cpp
@@ -27582,6 +27601,8 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(getfame, "?"),
 	BUILDIN_DEF(getfamerank, "?"),
 	BUILDIN_DEF(isdead, "?"),
+	BUILDIN_DEF(macro_detector, "?"),
+
 #include "../custom/script_def.inc"
 
 	{NULL,NULL,NULL},

+ 0 - 4
src/map/status.cpp

@@ -11257,7 +11257,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			break;
 		case SC_JAILED:
 			// Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time.
-			tick = val1>0?1000:250;
 			if (sd) {
 				if (sd->mapindex != val2) {
 					int pos =  (bl->x&0xFFFF)|(bl->y<<16), // Current Coordinates
@@ -13275,9 +13274,6 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
 			}
 			break;
 		case SC_JAILED:
-			if(tid == INVALID_TIMER)
-				break;
-		  	// Natural expiration.
 			if(sd && sd->mapindex == sce->val2)
 				pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT);
 			break; // Guess hes not in jail :P