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

Следующая улучшенная версия менеджера событий ver1 подошла к выходу.


1. Решена проблема функциональной атомарности.
2. Вызывать события можно прямо из метода Create любого объекта независимо от его позиции в иерархии создания объектов в комнате. Это не маловажный нюанс, т.к. не обязательно создавать менеджер событий самым первым в комнате, тем самым повышается гибкость использования скрипта. Но вызов события должен быть обязательно "отложенный" т.е. events_queque_publish
Пример:

//метод Create

events_listen("kuku", function(_data) 
{
	trace("получил данные = ", _data);
});

events_queque_publish("kuku", "отправка всем данных");

Данное сообщение получат все объекты кто подписался на событие kuku

 

//*******************************************************************
//Site:   MidaDev.ru
//Author: Dimusikus
//EMail:  Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.
//*******************************************************************
globalvar events_debug;				//вкл режим отладки, в структуру помещается доп инфа об объекте
events_debug = true;				

globalvar events_array;				//глобальная структура событий
events_array = {};					//создаём структуру

globalvar events_manager_obj;		//объект, используется для alarm что бы отправлять отложенные сообщения подписчикам
events_manager_obj = undefined;		//

//индекс объекта на котором остановилась отправка отложки
globalvar _events_queque_index;		
_events_queque_index=-1;			

//массив выделенных подписчиков по определённому сообщению
globalvar _events_queque_listens_array; 
_events_queque_listens_array=undefined; 

//массив/пул сообщений, которые отправляются подписчикам строго по очереди
globalvar _events_pool_event_array; 
_events_pool_event_array=[]; 
//массив/пул передаваемых данных, которые отправляются подписчикам
globalvar _events_pool_data_array; 
_events_pool_data_array=[]; 

//*******************************************************************
//данные будут хранится в виде структуры
enum evenum
{
    _id      = 0,	//айди объекта
    _func    = 1,	//функция которую он просит у себя вызвать
	_name_object =2 //имя объекта, используется в основном для отладки
}

//*******************************************************************
//подписка на событие, т.е. добавить объект/подписчика в массив событий
function events_listen(_event, _func) 
{
    //если такого эвента еще не создано
	if (is_undefined(events_array[$ _event])) 
	{	//то создаём его
        events_array[$ _event] = [];
    } 
	else 
	//если уже подписан то выходим 
	if (events_is_listen(id, _event) != -1) then return;
    
	var _str="";
	if events_debug then _str = object_get_name(object_index);
	//добавляем в массив нашего слушателя
    array_push(events_array[$ _event], [id, _func, _str ]);	
}

//*******************************************************************
//проверка не подписан ли уже объект на данное событие
function events_is_listen(_id, _event) 
{
    //бежим по всему списку эвентов
	for (var i = 0; i < array_length(events_array[$ _event]); i += 1) 
	{
        //если таковой есть то возвращаем его индекс
		if (events_array[$ _event][i][evenum._id] == _id) then return i; //есть такой, возвращаем его индекс       
    } 
    //нету такого, возвращаем отрицательный ответ
	return -1;
}

//*******************************************************************
//отправка сообщения одному слушателю
//внутренняя функция, не для внешнего вызова
function events_publishing(_index, _array, _data)
{
	//если объект существует то отправляем ему сообщение
	if (instance_exists(_array[_index][evenum._id])) then 
	{
		_array[_index][evenum._func](_data);
		return true;
	}    
	//иначе удаляем этого товарища из массива к едрени
	else
	{		
		if events_debug then trace("EVENTMANAGER: объедок выбыл, удаляем = ", _array[_index][evenum._name_object], "_", _array[_index][evenum._id]);		
		
		array_delete(_array, _index, 1); 		
		return false;
	}
}

//*******************************************************************
//отправляется сообщение всем подписанным на него объектам/подписчикам
function events_publish(_event, _data) 
{
	//получаем массив подписчиков на данный	 EVENT
    var _listens_array = events_array[$ _event];
    
    //если слушателей нет то выходим
	if (is_undefined(_listens_array)) then return;    
    
	//пробегаемся по массиву слушателей, двигаемся обязательно с конца к началу
	//что бы по ходу "пьессы" удалять не существующих уже слушателей из массива
	//и при удалении(массив сдвигается) не пропустить слушателей
    for (var i = (array_length(_listens_array) - 1); i >= 0; i -= 1) 
	{
		events_publishing(i, _listens_array, _data)
    }    
}

//*******************************************************************
//инициализация необходимых переменных для отложенной отправки
function events_queque_initial(_event)
{
	//получаем массив подписчиков на данный	 EVENT
	_events_queque_listens_array = events_array[$ _event];    
		
	//если слушателей нет в априоре вообще, то выходим
	if (is_undefined(_events_queque_listens_array)) then return false;
	
	//получаем количество подписчиков
	_events_queque_index = array_length(_events_queque_listens_array) - 1;
		
	return true;
}
//*******************************************************************
//выполняется ОТЛОЖЕННАЯ отправка через таймер(объекта), сообщения всем объектам/подписчикам
function events_queque_publish(_event, _data) 
{	
	//если индекс объекта в массиве = -1 значит это первая инициализация работы отложки
	if _events_queque_index <= -1 then
	{
		//if instance_exists(events_manager_obj)==false then events_manager_obj = instance_create_depth(0,0,0,obj_event_manager);
		if is_undefined(events_manager_obj)==true then events_manager_obj = instance_create_depth(0,0,0,obj_event_manager);		
		
		//инициализация необходимых переменных для отложенной отправки
		//if events_queque_initial(_event) == false then return;
						
		//сохраняем данные отправки(событие, данные), будут использоваться в функции отложенной отправки
		array_push(_events_pool_event_array, _event);
		array_push(_events_pool_data_array,  _data);
				
		//устарело, неактуально
		//запускаем начало первой отложенной отправки НЕМЕДЛЕННО
		//events_queque_step();
		
		//включаем таймер #1(иниц переменных), т.е., ТОЛЬКО на следующем шаге начнётся отложенная отправка
		events_manager_obj.alarm[1] = 1;
	}
	else 
	//ага, зафиксирована попытка отправить еще сообщения когда предыдущий сеанс отправки еще не завершен
	//помещаем данные в пул, в очередь, пусть ждут своего "часа"
	{
		array_push(_events_pool_event_array, _event);
		array_push(_events_pool_data_array,  _data);
	}	
}
//*******************************************************************
//вызывается из объекта из таймера каждый шаг
//ОТЛОЖЕННАЯ отправка сообщения всем объектам/подписчикам
//в массиве _events_queque_listens_array уже содержатся все отобранные слушатели для нужного события
function events_queque_step()
{	
	//делаем первую посылку	
	do
	{	//заведомо заранее делаем минусовку индекса
	   _events_queque_index -= 1;
	   //проверяем кооректность отправки посылки объету, если всё норм то брэкуем цикл DO
	   if (events_publishing(_events_queque_index+1, _events_queque_listens_array, array_get(_events_pool_data_array,0))==true) then break;
	   //нет не всё номано, видимо объект исчес/уничт, 
	   //проверяем индекс в массиве и если мы еще не достигли конца то повторяем посыл уже для следующего объекта в массиве
	   //до тех пор пока отправка не завершится удачно
	}until (_events_queque_index <= -1)
	
	//уменьшаем индекс
	//_events_queque_index -= 1;
	//запускаем алярм в объекте, который будет вызывать эту же функцию events_queque_publish каждый step шаг
	events_manager_obj.alarm[0] = 1;
	
	//если закончили отправлять сообщения
	if _events_queque_index <= -1 then
	{	//очищаем первое знчение в массиве
		events_array_pool_delete(0);
		//чистим массив объектов
		_events_queque_listens_array = undefined;		
		//отключаем таймер
		events_manager_obj.alarm[0] = -1;
		
		//если массив/пул сообщений еще не пуст, значит есть еще сообщения для отправки
		if array_length(_events_pool_event_array) >= 1 then 
		{			
			if events_queque_initial(array_get(_events_pool_event_array,0)) == false then
			{//нету желающих получать сообщения
				events_array_pool_delete(0);
				return;
			}
			//запускаем таймер и начинаем по новой но уже для следующего EVENTа
			events_manager_obj.alarm[0] = 1;
		}		
	}
}
//*******************************************************************
//очистка пула по передаваемому индексу
function events_array_pool_delete(_i=0)
{
	array_delete(_events_pool_data_array, _i, 1);
	array_delete(_events_pool_event_array, _i, 1);
}
//*******************************************************************
//отписать объект/подписчик по его айди от события
function events_unlisten_id(_id, _event) 
{
    //если такого эвента не существует то выходим
	if (is_undefined(events_array[$ _event])) return;
    
	//получаем позицию объекта в массиве 
    var _index = events_is_listen(_id, _event);
	
    //удаляем его если нашелся
	if (_index != -1) then   array_delete(events_array[$ _event], _index, 1);    
}

//*******************************************************************
//отписать текущий объект/подписчик от события
function events_unlisten(_event) {    events_unlisten_id(id, _event);   }

//*******************************************************************
//отписать объект/подписчик от всех событий
function events_unlisten_all_id(_id=id)
{
    //получаем все значения имён массива
	var _keys_array = variable_struct_get_names(events_array);
	
    for (var i = (array_length(_keys_array) - 1); i >= 0; i -= 1) 
	{
        events_unlisten_id(_id, _keys_array[i]);
    }
}
//*******************************************************************
//удаляем все позиции/подписчиков с данным эвентом
function events_remove_event(_event) 
{
    if (variable_struct_exists(events_array, _event)) then  variable_struct_remove(events_array, _event);    
}

//*******************************************************************
//удаляем полностью  массив сообщений и подписчиков
function events_remove_all_events()
{
    delete events_array;
    events_array = {};
}

//*******************************************************************
//очистка массива от не существующих объектов/подписчиков
function events_remove_dead_instances()
{
    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);
            }
        }
    }
}

Данный менеджер тоже не лишен недостатков.

К примеру данный менеджер событий может использоваться только как один для всего происходящего в приложении. А иногда для гибкости и оптимальности бывает необходимы иметь несколько разных менеджеров, например для GUI, для системных надобностей, для юнитов, и для других происходящих групп событий который друг к другу мало чем имеют отношение.

Ну чтож,  задача поставлена будем решать в следующих статьях.