Менеджер событий ver3

Переделал менеджер событий EventManager ver2, исправлены некоторые ошибки(отложенная отправка проваливалась если на событие не было подписчиков).

Приемущества от предыдущей версии:
 - Весь код засунут в конструктор
 - отсюда, возможно создать не ограниченное количество менеджеров, например для Gui, System, GameEvents и других назначений.

Минусы:
- отладка!, если что то пошло не так, отладить или отлавливать логическую ошибку в МОРЕ событий  это просто АД. В код я засунул по возможности где можно выводы в лог отладчика, это хоть как то да помогает найти проблемные места.

 

//Site:   MidaDev.ru

//данные будут хранится в виде структуры
enum evum
{
    _id          =0, //айди объекта
    _func        =1, //функция которую он просит у себя вызвать
	_name_object =2, //"тип" объекта, используется в основном для отладки когда включен debug
	_name_is     =3  //имя объекта, используется в основном для отладки когда включен debug
}
#macro EMMACRO "EventManager(" + my_name_is +"): "
//*******************************************************************
function Events(_name) constructor 
{
	#region [Constructor]
	my_name_is = _name;		//имя, назначение класса
	events_array = {};		//глобальная структура событий	
	debug = true;			//вкл режим отладки, в структуру помещается доп инфа об объекте
	obj_alarm = noone;		//объект, используется как alarm для отсчёта шагов при отложенной отправке писем
	queue_index=-1;			//индекс объекта на котором остановилась отправка отложки
	queue_array=undefined;	//массив выделенных подписчиков по определённому EVENTу
	//массивы/пул сообщений, которые отправляются подписчикам строго по очереди
	pool_event_array=[]; pool_data_array=[];	
	_log("=================================================================================");
	trace("Сосздан глобальный Менеджер событий типа = ", my_name_is);	
	_log("=================================================================================");
	#endregion
	
	#region [Methods] 
	//*******************************************************************
	//МЕТОДЫ
	//*******************************************************************
	static trace_debug = function(a="",b="",c="",d="",e="",f="",g=""){ if debug then trace(EMMACRO, a,b,c,d,e,f,g);};
	static get_my_name_is = function(){ try{ return other.my_name_is}catch(m){ return ""} };
	static get_obj_name = function(){ return object_get_name(other.object_index); };
	static get_obj_full_name = function(){ return get_obj_name() + "(" + get_my_name_is() + ")"; };
	//*******************************************************************
	static get_all_info_events_array = function(_arr)	{	var _str="";		for(i=0; i < (array_length(_arr)); i++){_str+= "[" + _arr[i][evum._name_object] + "(" + _arr[i][evum._name_is] + ")]  " ;}		return _str;	}
	//*******************************************************************
	//подписка на событие, т.е. добавить объект/подписчика в массив событий
	static subscribe = function(_event, _func) 
	{	if _event == "" then{ show_message(EMMACRO + "subscribe() - Попытка подписаться на пустой эвент = ``"); return;};
		//если такого эвента еще не создано, то создаём ехо
		if (is_undefined(events_array[$ _event])) events_array[$ _event] = [];
		//если он уже подписан, то выходим из функции
		else if (is_subscribe(other.id, _event) != -1) then{show_message(EMMACRO + "subscribe() - попытка подписаться дважды на одно и тоже событие"); return;}		
		trace_debug("Объект=", get_obj_full_name(), ", подписался на событие=", _event);		
		array_push(events_array[$ _event], [other.id, _func, get_obj_name(), get_my_name_is() ]);	//добавляем в массив нашего слушателя
	}
	//*******************************************************************
	//проверка не подписан ли уже объект на данное событие
	static is_subscribe = function(_id, _event) 
	{   //бежим по всему списку эвентов
		for (var i = 0; i < array_length(events_array[$ _event]); i += 1)
			if (events_array[$ _event][i][evum._id] == _id) then return i; //есть такой, возвращаем его индекс		 
		//нету такого, возвращаем отрицательный ответ
		return -1;
	}
	//*******************************************************************
	//отправка сообщения одному слушателю
	//внутренняя функция, не для вызова из вне
	static publishing = function(_index, _array, _data)
	{	trace_debug("============================publishing непосредственно отправка==========================");
		//если объект существует то отправляем ему сообщение
		if (instance_exists(_array[_index][evum._id])) then {	_array[_index][evum._func](_data);	return true; }
		else //иначе удаляем этого товарища из массива к едрени
		{	trace_debug(" объедок выбыл, удаляем = ", _array[_index][evum._name_object], " , ", _array[_index][evum._id], " , ", _array[_index][evum._name_is]);
			array_delete(_array, _index, 1); 		
			return false;
		}
	}
	//*******************************************************************
	//отправляется сообщение всем подписанным на него объектам/подписчикам
	static publish = function(_event, _data) 
	{	trace_debug("============================publish==============================");
		trace_debug("Объект=", get_obj_full_name(), ", шлёт событие=", _event, " с данными=", _data);
		//получаем массив подписчиков на данный	 EVENT
	    var _listens_array = events_array[$ _event];
	    //если слушателей нет то выходим
		if (is_undefined(_listens_array)) then {trace_debug("слушателей нету"); return;}
		
		//пробегаемся по массиву слушателей, двигаемся обязательно с конца к началу
	    for (var i = (array_length(_listens_array) - 1); i >= 0; i -= 1)
		{	trace_debug(" отправляем событие: ", _event, " , подписчику: ", _listens_array[i][evum._name_object], "(", _listens_array[i][evum._name_is], ")", "данные: ", _data);
			publishing(i, _listens_array, _data);
		}
	}
	//*******************************************************************	
	//инициализация необходимых переменных для отложенной отправки alarm[0]
	static queue_initial = function()
	{	trace_debug("============================queue_initial   alarm[0]=======================");		
		trace_debug("Ожидающих событий:", array_length(pool_event_array), " , очередь событий: ", array_to_string(pool_event_array) );
		_event = array_get(pool_event_array, 0);
		trace_debug(" будем отправлять событие: ", _event,", с данными=", array_get(pool_data_array, 0) );
		//получаем массив подписчиков на данный	 EVENT
		queue_array = events_array[$ _event];		
		//если слушателей нет в априоре вообще, то выходим
		if (is_undefined(queue_array)) then
		{	trace_debug("Слушателей почемуто тютю");
			return false;}
		//получаем количество подписчиков
		queue_index = array_length(queue_array) - 1;
		trace_debug("Количество слушателей: ", queue_index+1, "  список: ", get_all_info_events_array(queue_array));		
		return true;
	}
	//*******************************************************************
	//выполняется ОТЛОЖЕННАЯ отправка через таймер(объекта), сообщения всем объектам/подписчикам
	static queue_publish = function(_event, _data) 
	{	trace_debug("============================queue_publish==============================");
		trace_debug("Обект = ", get_obj_full_name(), " , шлёт QUEUE событие=", _event, " с данными=", _data);
		//если индекс объекта в массиве = -1 значит это первая инициализация работы отложки
		if queue_index <= -1 then
		{	//создание вспомогательного объекта если его еще нет
			if instance_exists(obj_alarm)==false then {obj_alarm = instance_create_depth(0,0,0,obj_EM2); obj_alarm.eventa = self;}
			obj_alarm.alarm[0] = 1; //включаем таймер #0(иниц переменных), т.е., ТОЛЬКО на следующем шаге начнётся отложенная отправка			
		}
		//сохраняем данные отправки(событие, данные), будут использоваться в функции отложенной отправки
		array_push(pool_event_array, _event);	array_push(pool_data_array,  _data);
		trace_debug("Итого уже отложенных событий:", array_length(pool_event_array), " , события в массиве: ", array_to_string(pool_event_array) );
	}	
	//*******************************************************************
	//вызывается из объекта из таймера[1] каждый шаг
	//ОТЛОЖЕННАЯ отправка сообщения всем объектам/подписчикам
	//в массиве queque_array уже содержатся все отобранные слушатели для нужного события
	//******************************************************************* alarm[1]
	static queue_step = function()
	{	trace_debug("============================queue_step   alarm[1]=========================");
		//делаем первую посылку	
		//проверяем кооректность отправки посылки объету, если всё норм то брэкуем цикл while		
		while(queue_index >= 0)
		{
			if publishing(queue_index--, queue_array, array_get(pool_data_array,0)) then break;
			/*var _r= publishing(queue_index--, queue_array, array_get(pool_data_array,0));
			if _r  then trace_debug("отправка состоялась");
			if !_r then trace_debug("отправка НЕсостоялась");
			if (_r==true) then break;*/
		}
		obj_alarm.alarm[1] = 1;
		
		if queue_index <= -1 then	//если список подписчиков для отправки закончился
		{	array_pool_delete(0);	 //очищаем (верний/этот) эвент
			queue_array = undefined; //чистим массив объектов			
			obj_alarm.alarm[1] = -1; //отключаем таймер
			
			//если массив/пул/очередь сообщений еще не пуст, значит есть еще сообщения для отправки
			if array_length(pool_event_array) >= 1 then 
			{	
				obj_alarm.alarm[1] = 1;	//запускаем таймер и начинаем по новой но уже для следующего EVENTа
				//цикл если queue_initial()=false
				while(!queue_initial(array_get(pool_event_array,0)))
				{	//видимо нет желающих получать сообщения(нет подписчиков)
					array_pool_delete(0);
					//если так неудачно весь массив пула испробовали, то, выкл таймер и брекуем из цикла while
					if array_length(pool_event_array) <=0 then {	obj_alarm.alarm[1] = -1;break;	}
					//иначе идём опять на цикл к следующему элементу из пула
				}				
			}		
		}		
	}
	//*******************************************************************
	//очистка пула по передаваемому индексу
	static array_pool_delete = function(_i=0){	array_delete(pool_data_array, _i, 1);	array_delete(pool_event_array, _i, 1);	};		
	//*******************************************************************
	//отписать объект/подписчик по его айди от события
	static unsubscribe_id = function(_id, _event) 
	{	if (is_undefined(events_array[$ _event])) return;	//если такого эвента не существует то выходим		
		var _index = is_subscribe(_id, _event);				//получаем позицию объекта в массиве 		
		if (_index != -1) then array_delete(events_array[$ _event], _index, 1);	//удаляем его если нашелся
	}	
	//*******************************************************************
	//отписать объект/подписчик от всех событий
	static unsubscribe_all_id = function(_id=id)
	{	var _keys_array = variable_struct_get_names(events_array); //получаем все значения имён массива	
		for (var i = (array_length(_keys_array) - 1); i >= 0; i -= 1)	unsubscribe_id(_id, _keys_array[i]);		
	}	
	//*******************************************************************
	//удаляем все позиции/подписчиков с данным эвентом
	static unsubscribe_event = function(_event)	{   if (variable_struct_exists(events_array, _event)) then  variable_struct_remove(events_array, _event); }	
	//*******************************************************************
	//удаляем полностью  массив сообщений и подписчиков
	static events_remove_all_events = function(){	delete events_array;	events_array = {};	}
	//*******************************************************************
	//очистка массива от не существующих объектов/подписчиков
	static unsubscribe_dead_instances = function()
	{   var _keys_array = variable_struct_get_names(events_array);
	    for (var i = 0; i < array_length(_keys_array); i += 1) 
		{	var _keys_array_subs = events_array[$ _keys_array[i]];
			for (var j = (array_length(_keys_array_subs) - 1); j >= 0; j -= 1) if (!instance_exists(_keys_array_subs[j][0])) array_delete(events_array[$ _keys_array[i]], j, 1);				        
		}
	}
	#endregion
	
//конец батьки	
}
//*******************************************************************
globalvar EVENTGUI;
globalvar EVENTDATA;

EVENTGUI = new Events("GuiEvents");
EVENTDATA= new Events("DataEvents");
//****************************************************************

 

В конце скрипта создаются две глобальных переменных на различные события.
В событии создания объектов можно выполнить подписку на нужные события:

/// @description test01
//Site:   MidaDev.ru
//Author: Dimusikus

EVENTGUI.subscribe("Click", function(_data) 
{
	if is_array(_data) then
	{
		trace("получил click = [0] ", _data[0]);
		trace("получил click = [1] ", _data[1]);
	}
	else trace("получил click = ", _data);
	
});

EVENTDATA.subscribe("PUT", function(_data) 
{
	trace("получил PUT = ", _data);
	
});

 Любой объект может отправить событие в менеджер командами:

//прямая/сразу отправка сообщения 
EVENTGUI.publish("Click", "mouse2");

//отложенная отправка, т.е. на следующих шагах игры
EVENTGUI.queue_publish("Click", "mouse2");