Browse Source

Follow up to 9738c82
* Fixed some more memory leaks.
* Added a few checks that were left out/removed by mistake.

aleos89 9 years ago
parent
commit
b8f6e16f9d
3 changed files with 63 additions and 5 deletions
  1. 9 3
      src/map/npc.c
  2. 51 1
      src/map/script.c
  3. 3 1
      src/map/script.h

+ 9 - 3
src/map/npc.c

@@ -1278,11 +1278,14 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd)
  *------------------------------------------*/
 int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 {
+	struct block_list *target = map_id2bl(id);
+
 	nullpo_retr(1, sd);
 
 	if( id != sd->npc_id ){
-		TBL_NPC* nd_sd=(TBL_NPC*)map_id2bl(sd->npc_id);
-		TBL_NPC* nd=(TBL_NPC*)map_id2bl(id);
+		TBL_NPC* nd_sd = (TBL_NPC*)map_id2bl(sd->npc_id);
+		TBL_NPC* nd = BL_CAST(BL_NPC, target);
+
 		ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n",
 			nd_sd?(char*)nd_sd->name:"'Unknown NPC'", (int)sd->npc_id,
 			nd?(char*)nd->name:"'Unknown NPC'", (int)id);
@@ -1290,7 +1293,7 @@ int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 	}
 
 	if(id != fake_nd->bl.id) { // Not item script
-		if ((npc_checknear(sd,map_id2bl(id))) == NULL){
+		if ((npc_checknear(sd, target)) == NULL) {
 			ShowWarning("npc_scriptcont: failed npc_checknear test.\n");
 			return 1;
 		}
@@ -1305,6 +1308,9 @@ int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 	if( sd->progressbar.npc_id && DIFF_TICK(sd->progressbar.timeout,gettick()) > 0 )
 		return 1;
 
+	if (!sd->st)
+		return 1;
+
 	if( closing && sd->st && sd->st->state == CLOSE )
 		sd->st->state = END;
 

+ 51 - 1
src/map/script.c

@@ -3391,6 +3391,8 @@ void script_free_code(struct script_code* code)
 {
 	nullpo_retv(code);
 
+	if (code->instances)
+		script_stop_instances(code);
 	script_free_vars(code->local.vars);
 	if (code->local.arrays)
 		code->local.arrays->destroy(code->local.arrays, script_free_array_db);
@@ -3424,6 +3426,14 @@ struct script_state* script_alloc_state(struct script_code* rootscript, int pos,
 	st->oid = oid;
 	st->sleep.timer = INVALID_TIMER;
 	st->npc_item_flag = battle_config.item_enabled_npc;
+	
+	if( st->script->instances != USHRT_MAX )
+		st->script->instances++;
+	else {
+		struct npc_data *nd = map_id2nd(oid);
+
+		ShowError("Over 65k instances of '%s' script are being run!\n",nd ? nd->name : "unknown");
+	}
 
 	if (!st->script->local.vars)
 		st->script->local.vars = i64db_alloc(DB_OPT_RELEASE_DATA);
@@ -3442,9 +3452,20 @@ struct script_state* script_alloc_state(struct script_code* rootscript, int pos,
 void script_free_state(struct script_state* st)
 {
 	if (idb_exists(st_db, st->id)) {
+		struct map_session_data *sd = st->rid ? map_id2sd(st->rid) : NULL;
+
 		if (st->bk_st) // backup was not restored
 			ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
 
+		if (sd && sd->st == st) { // Current script is aborted.
+			if(sd->state.using_fake_npc) {
+				clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd);
+				sd->state.using_fake_npc = 0;
+			}
+			sd->st = NULL;
+			sd->npc_id = 0;
+		}
+
 		if (st->sleep.timer != INVALID_TIMER)
 			delete_timer(st->sleep.timer, run_script_timer);
 		if (st->stack) {
@@ -3456,7 +3477,7 @@ void script_free_state(struct script_state* st)
 			ers_free(stack_ers, st->stack);
 			st->stack = NULL;
 		}
-		if (st->script) {
+		if (st->script && st->script->instances != USHRT_MAX && --st->script->instances == 0) {
 			if (st->script->local.vars && !db_size(st->script->local.vars)) {
 				script_free_vars(st->script->local.vars);
 				st->script->local.vars = NULL;
@@ -3942,6 +3963,23 @@ void run_script(struct script_code *rootscript, int pos, int rid, int oid)
 	run_script_main(st);
 }
 
+void script_stop_instances(struct script_code *code) {
+	DBIterator *iter;
+	struct script_state* st;
+
+	if( !active_scripts )
+		return; // Don't even bother.
+
+	iter = db_iterator(st_db);
+
+	for( st = dbi_first(iter); dbi_exists(iter); st = dbi_next(iter) ) {
+		if( st->script == code )
+			script_free_state(st);
+	}
+
+	dbi_destroy(iter);
+}
+
 /*==========================================
  * Timer function for sleep
  *------------------------------------------*/
@@ -9054,6 +9092,18 @@ BUILDIN_FUNC(end)
 
 	st->state = END;
 
+	if (st->stack->defsp >= 1 && st->stack->stack_data[st->stack->defsp-1].type == C_RETINFO) {
+		int i;
+
+		for(i = 0; i < st->stack->sp; i++) {
+			if (st->stack->stack_data[i].type == C_RETINFO) { // Grab the first, aka the original
+				struct script_retinfo *ri = st->stack->stack_data[i].u.ri;
+				st->script = ri->script;
+				break;
+			}
+		}
+	}
+
 	if( st->mes_active )
 		st->mes_active = 0;
 

+ 3 - 1
src/map/script.h

@@ -234,6 +234,7 @@ struct script_code {
 	int script_size;
 	unsigned char* script_buf;
 	struct reg_db local;
+	unsigned short instances;
 };
 
 struct script_stack {
@@ -256,7 +257,7 @@ struct script_state {
 	int pos;
 	enum e_script_state state;
 	int rid,oid;
-	struct script_code *script, *scriptroot;
+	struct script_code *script;
 	struct sleep_data {
 		int tick,timer,charid;
 	} sleep;
@@ -319,6 +320,7 @@ void pop_stack(struct script_state* st, int start, int end);
 int run_script_timer(int tid, unsigned int tick, int id, intptr_t data);
 void run_script_main(struct script_state *st);
 
+void script_stop_instances(struct script_code *code);
 struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n);
 void script_free_code(struct script_code* code);
 void script_free_vars(struct DBMap *storage);