Ports event system from Bay

This commit is contained in:
Yoshax
2016-06-01 22:05:04 +01:00
parent 2c95126a00
commit f75d509e7f
51 changed files with 814 additions and 161 deletions
-2
View File
@@ -28,7 +28,6 @@ var/global/datum/getrev/revdata = new()
world.log << branch
world.log << date
world.log << revision
return
client/verb/showrevinfo()
set category = "OOC"
@@ -43,4 +42,3 @@ client/verb/showrevinfo()
src << revdata.revision
else
src << "Revision unknown"
return
+1
View File
@@ -71,6 +71,7 @@
/datum/mind/New(var/key)
src.key = key
..()
/datum/mind/proc/transfer_to(mob/living/new_character)
if(!istype(new_character))
+11
View File
@@ -0,0 +1,11 @@
/****************
* Debug Support *
****************/
var/datum/all_observable_events/all_observable_events = new()
/datum/all_observable_events
var/list/events
/datum/all_observable_events/New()
events = list()
..()
+1
View File
@@ -0,0 +1 @@
#define CANCEL_MOVE_EVENT -55
+15
View File
@@ -0,0 +1,15 @@
// Observer Pattern Implementation: Destroyed
// Registration type: /datum
//
// Raised when: A /datum instance is destroyed.
//
// Arguments that the called proc should expect:
// /datum/destroyed_instance: The instance that was destroyed.
var/decl/observ/destroyed/destroyed_event = new()
/decl/observ/destroyed
name = "Destroyed"
/datum/Destroy()
destroyed_event.raise_event(src)
. = ..()
+35
View File
@@ -0,0 +1,35 @@
// Observer Pattern Implementation: Direction Set
// Registration type: /atom
//
// Raised when: An /atom changes dir using the set_dir() proc.
//
// Arguments that the called proc should expect:
// /atom/dir_changer: The instance that changed direction
// /old_dir: The dir before the change.
// /new_dir: The dir after the change.
var/decl/observ/dir_set/dir_set_event = new()
/decl/observ/dir_set
name = "Direction Set"
expected_type = /atom
/decl/observ/dir_set/register(var/atom/dir_changer, var/datum/listener, var/proc_call)
. = ..()
// Listen to the parent if possible.
if(. && istype(dir_changer.loc, /atom/movable)) // We don't care about registering to turfs.
register(dir_changer.loc, dir_changer, /atom/proc/recursive_dir_set)
/*********************
* Direction Handling *
*********************/
/atom/movable/Entered(var/atom/movable/am, atom/old_loc)
. = ..()
if(. != CANCEL_MOVE_EVENT && dir_set_event.has_listeners(am))
dir_set_event.register(src, am, /atom/proc/recursive_dir_set)
/atom/movable/Exited(var/atom/movable/am, atom/old_loc)
. = ..()
dir_set_event.unregister(src, am, /atom/proc/recursive_dir_set)
+38
View File
@@ -0,0 +1,38 @@
// Observer Pattern Implementation: Equipped
// Registration type: /mob
//
// Raised when: A mob equips an item.
//
// Arguments that the called proc should expect:
// /mob/equipper: The mob that equipped the item.
// /obj/item/item: The equipped item.
// slot: The slot equipped to.
var/decl/observ/mob_equipped/mob_equipped_event = new()
/decl/observ/mob_equipped
name = "Mob Equipped"
expected_type = /mob
// Observer Pattern Implementation: Equipped
// Registration type: /obj/item
//
// Raised when: A mob equips an item.
//
// Arguments that the called proc should expect:
// /obj/item/item: The equipped item.
// /mob/equipper: The mob that equipped the item.
// slot: The slot equipped to.
var/decl/observ/item_equipped/item_equipped_event = new()
/decl/observ/item_equipped
name = "Item Equipped"
expected_type = /obj/item
/********************
* Equipped Handling *
********************/
/obj/item/equipped(var/mob/user, var/slot)
. = ..()
mob_equipped_event.raise_event(user, src, slot)
item_equipped_event.raise_event(src, user, slot)
+18
View File
@@ -0,0 +1,18 @@
/atom/movable/proc/recursive_move(var/atom/movable/am, var/old_loc, var/new_loc)
moved_event.raise_event(src, old_loc, new_loc)
/atom/movable/proc/move_to_destination(var/atom/movable/am, var/old_loc, var/new_loc)
var/turf/T = get_turf(new_loc)
if(T && T != loc)
forceMove(T)
/atom/proc/recursive_dir_set(var/atom/a, var/old_dir, var/new_dir)
set_dir(new_dir)
/proc/register_all_movement(var/event_source, var/listener)
moved_event.register(event_source, listener, /atom/movable/proc/recursive_move)
dir_set_event.register(event_source, listener, /atom/proc/recursive_dir_set)
/proc/unregister_all_movement(var/event_source, var/listener)
moved_event.unregister(event_source, listener, /atom/movable/proc/recursive_move)
dir_set_event.unregister(event_source, listener, /atom/proc/recursive_dir_set)
+21
View File
@@ -0,0 +1,21 @@
// Observer Pattern Implementation: Logged in
// Registration type: /mob
//
// Raised when: A mob logs in (not client)
//
// Arguments that the called proc should expect:
// /mob/joiner: The mob that has logged in
var/decl/observ/logged_in/logged_in_event = new()
/decl/observ/logged_in
name = "Logged In"
expected_type = /mob
/*****************
* Login Handling *
*****************/
/mob/Login()
..()
logged_in_event.raise_event(src)
+52
View File
@@ -0,0 +1,52 @@
// Observer Pattern Implementation: Moved
// Registration type: /atom/movable
//
// Raised when: An /atom/movable instance has moved using Move() or forceMove().
//
// Arguments that the called proc should expect:
// /atom/movable/moving_instance: The instance that moved
// /atom/old_loc: The loc before the move.
// /atom/new_loc: The loc after the move.
var/decl/observ/moved/moved_event = new()
/decl/observ/moved
name = "Moved"
expected_type = /atom/movable
/decl/observ/moved/register(var/atom/movable/mover, var/datum/listener, var/proc_call)
. = ..()
// Listen to the parent if possible.
if(. && istype(mover.loc, expected_type))
register(mover.loc, mover, /atom/movable/proc/recursive_move)
/********************
* Movement Handling *
********************/
/atom/Entered(var/atom/movable/am, var/atom/old_loc)
. = ..()
moved_event.raise_event(am, old_loc, am.loc)
/atom/movable/Entered(var/atom/movable/am, atom/old_loc)
. = ..()
if(moved_event.has_listeners(am))
moved_event.register(src, am, /atom/movable/proc/recursive_move)
/atom/movable/Exited(var/atom/movable/am, atom/old_loc)
. = ..()
moved_event.unregister(src, am, /atom/movable/proc/recursive_move)
// Entered() typically lifts the moved event, but in the case of null-space we'll have to handle it.
/atom/movable/Move()
var/old_loc = loc
. = ..()
if(. && !loc)
moved_event.raise_event(src, old_loc, null)
/atom/movable/forceMove(atom/destination)
var/old_loc = loc
. = ..()
if(. && !loc)
moved_event.raise_event(src, old_loc, null)
+238
View File
@@ -0,0 +1,238 @@
//
// Observer Pattern Implementation
//
// Implements a basic observer pattern with the following main procs:
//
// /decl/observ/proc/is_listening(var/event_source, var/datum/listener, var/proc_call)
// event_source: The instance which is generating events.
// listener: The instance which may be listening to events by event_source
// proc_call: Optional. The specific proc to call when the event is raised.
//
// Returns true if listener is listening for events by event_source, and proc_call supplied is either null or one of the proc that will be called when an event is raised.
//
// /decl/observ/proc/has_listeners(var/event_source)
// event_source: The instance which is generating events.
//
// Returns true if the given event_source has any listeners at all, globally or to specific event sources.
//
// /decl/observ/proc/register(var/event_source, var/datum/listener, var/proc_call)
// event_source: The instance you wish to receive events from.
// listener: The instance/owner of the proc to call when an event is raised by the event_source.
// proc_call: The proc to call when an event is raised.
//
// It is possible to register the same listener to the same event_source multiple times as long as it is using different proc_calls.
// Registering again using the same event_source, listener, and proc_call that has been registered previously will have no additional effect.
// I.e.: The proc_call will still only be called once per raised event. That particular proc_call will only have to be unregistered once.
//
// When proc_call is called the first argument is always the source of the event (event_source).
// Additional arguments may or may not be supplied, see individual event definition files (destroyed.dm, moved.dm, etc.) for details.
//
// The instance making the register() call is also responsible for calling unregister(), see below for additonal details, including when event_source is destroyed.
// This can be handled by listening to the event_source's destroyed event, unregistering in the listener's Destroy() proc, etc.
//
// /decl/observ/proc/unregister(var/event_source, var/datum/listener, var/proc_call)
// event_source: The instance you wish to stop receiving events from.
// listener: The instance which will no longer receive the events.
// proc_call: Optional: The proc_call to unregister.
//
// Unregisters the listener from the event_source.
// If a proc_call has been supplied only that particular proc_call will be unregistered. If the proc_call isn't currently registered there will be no effect.
// If no proc_call has been supplied, the listener will have all registrations made to the given event_source undone.
//
// /decl/observ/proc/register_global(var/datum/listener, var/proc_call)
// listener: The instance/owner of the proc to call when an event is raised by any and all sources.
// proc_call: The proc to call when an event is raised.
//
// Works very much the same as register(), only the listener/proc_call will receive all relevant events from all event sources.
// Global registrations can overlap with registrations made to specific event sources and these will not affect each other.
//
// /decl/observ/proc/unregister_global(var/datum/listener, var/proc_call)
// listener: The instance/owner of the proc which will no longer receive the events.
// proc_call: Optional: The proc_call to unregister.
//
// Works very much the same as unregister(), only it undoes global registrations instead.
//
// /decl/observ/proc/raise_event(src, ...)
// Should never be called unless implementing a new event type.
// The first argument shall always be the event_source belonging to the event. Beyond that there are no restrictions.
/decl/observ
var/name = "Unnamed Event" // The name of this event, used mainly for debug/VV purposes. The list of event managers can be reached through the "Debug Controller" verb, selecting the "Observation" entry.
var/expected_type = /datum // The expected event source for this event. register() will CRASH() if it receives an unexpected type.
var/list/event_sources = list() // Associative list of event sources, each with their own associative list. This associative list contains an instance/list of procs to call when the event is raised.
var/list/global_listeners = list() // Associative list of instances that listen to all events of this type (as opposed to events belonging to a specific source) and the proc to call.
/decl/observ/New()
all_observable_events.events += src
. = ..()
/decl/observ/proc/is_listening(var/event_source, var/datum/listener, var/proc_call)
// Return whether there are global listeners unless the event source is given.
if (!event_source)
return !!global_listeners.len
// Return whether anything is listening to a source, if no listener is given.
if (!listener)
return global_listeners.len || (event_source in event_sources)
// Return false if nothing is associated with that source.
if (!(event_source in event_sources))
return FALSE
// Get and check the listeners for the reuqested event.
var/listeners = event_sources[event_source]
if (!(listener in listeners))
return FALSE
// Return true unless a specific callback needs checked.
if (!proc_call)
return TRUE
// Check if the specific callback exists.
var/list/callback = listeners[listener]
if (!callback)
return FALSE
return (proc_call in callback)
/decl/observ/proc/has_listeners(var/event_source)
return is_listening(event_source)
/decl/observ/proc/register(var/datum/event_source, var/datum/listener, var/proc_call)
// Sanity checking.
if (!(event_source && listener && proc_call))
return FALSE
if (istype(event_source, /decl/observ))
return FALSE
// Crash if the event source is the wrong type.
if (!istype(event_source, expected_type))
CRASH("Unexpected type. Expected [expected_type], was [event_source.type]")
// Setup the listeners for this source if needed.
var/list/listeners = event_sources[event_source]
if (!listeners)
listeners = list()
event_sources[event_source] = listeners
// Make sure the callbacks are a list.
var/list/callbacks = listeners[listener]
if (!callbacks)
callbacks = list()
listeners[listener] = callbacks
// If the proc_call is already registered skip
if(proc_call in callbacks)
return FALSE
// Add the callback, and return true.
callbacks += proc_call
return TRUE
/decl/observ/proc/unregister(var/event_source, var/datum/listener, var/proc_call)
// Sanity.
if (!(event_source && listener && (event_source in event_sources)))
return FALSE
// Return false if nothing is listening for this event.
var/list/listeners = event_sources[event_source]
if (!listeners)
return FALSE
// Remove all callbacks if no specific one is given.
if (!proc_call)
if(listeners.Remove(listener))
// Perform some cleanup and return true.
if (!listeners.len)
event_sources -= event_source
return TRUE
return FALSE
// See if the listener is registered.
var/list/callbacks = listeners[listener]
if (!callbacks)
return FALSE
// See if the callback exists.
if(!callbacks.Remove(proc_call))
return FALSE
if (!callbacks.len)
listeners -= listener
if (!listeners.len)
event_sources -= event_source
return TRUE
/decl/observ/proc/register_global(var/datum/listener, var/proc_call)
// Sanity.
if (!(listener && proc_call))
return FALSE
// Make sure the callbacks are setup.
var/list/callbacks = global_listeners[listener]
if (!callbacks)
callbacks = list()
global_listeners[listener] = callbacks
// Add the callback and return true.
callbacks |= proc_call
return TRUE
/decl/observ/proc/unregister_global(var/datum/listener, var/proc_call)
// Return false unless the listener is set as a global listener.
if (!(listener && (listener in global_listeners)))
return FALSE
// Remove all callbacks if no specific one is given.
if (!proc_call)
global_listeners -= listener
return TRUE
// See if the listener is registered.
var/list/callbacks = global_listeners[listener]
if (!callbacks)
return FALSE
// See if the callback exists.
if(!callbacks.Remove(proc_call))
return FALSE
if (!callbacks.len)
global_listeners -= listener
return TRUE
/decl/observ/proc/raise_event()
// Sanity
if (!args.len)
return FALSE
// Call the global listeners.
for (var/datum/listener in global_listeners)
var/list/callbacks = global_listeners[listener]
for (var/proc_call in callbacks)
// If the callback crashes, record the error and remove it.
try
call(listener, proc_call)(arglist(args))
catch (var/exception/e)
error("[e.name] - [e.file] - [e.line]")
error(e.desc)
unregister_global(listener, proc_call)
// Call the listeners for this specific event source, if they exist.
var/source = args[1]
if (source in event_sources)
var/list/listeners = event_sources[source]
for (var/datum/listener in listeners)
var/list/callbacks = listeners[listener]
for (var/proc_call in callbacks)
// If the callback crashes, record the error and remove it.
try
call(listener, proc_call)(arglist(args))
catch (var/exception/e)
error("[e.name] - [e.file] - [e.line]")
error(e.desc)
unregister(source, listener, proc_call)
return TRUE
+13
View File
@@ -0,0 +1,13 @@
//
// Observer Pattern Implementation: Scheduled task triggered
// Registration type: /datum/scheduled_task
//
// Raised when: When a scheduled task reaches its trigger time.
//
// Arguments that the called proc should expect:
// /datum/scheduled_task/task: The task that reached its trigger time.
var/decl/observ/task_triggered/task_triggered_event = new()
/decl/observ/task_triggered
name = "Task Triggered"
expected_type = /datum/scheduled_task
+38
View File
@@ -0,0 +1,38 @@
// Observer Pattern Implementation: Unequipped (dropped)
// Registration type: /mob
//
// Raised when: A mob unequips/drops an item.
//
// Arguments that the called proc should expect:
// /mob/equipped: The mob that unequipped/dropped the item.
// /obj/item/item: The unequipped item.
var/decl/observ/mob_unequipped/mob_unequipped_event = new()
/decl/observ/mob_unequipped
name = "Mob Unequipped"
expected_type = /mob
// Observer Pattern Implementation: Unequipped (dropped)
// Registration type: /obj/item
//
// Raised when: A mob unequips/drops an item.
//
// Arguments that the called proc should expect:
// /obj/item/item: The unequipped item.
// /mob/equipped: The mob that unequipped/dropped the item.
var/decl/observ/item_unequipped/item_unequipped_event = new()
/decl/observ/item_unequipped
name = "Item Unequipped"
expected_type = /obj/item
/**********************
* Unequipped Handling *
**********************/
/obj/item/dropped(var/mob/user)
..()
mob_unequipped_event.raise_event(user, src)
item_unequipped_event.raise_event(src, user)
+70
View File
@@ -0,0 +1,70 @@
var/list/global_listen_count = list()
var/list/event_sources_count = list()
var/list/event_listen_count = list()
/decl/observ/destroyed/raise_event()
. = ..()
if(!.)
return
var/source = args[1]
if(global_listen_count[source])
cleanup_global_listener(source, global_listen_count[source])
if(event_sources_count[source])
cleanup_source_listeners(source, event_sources_count[source])
if(event_listen_count[source])
cleanup_event_listener(source, event_listen_count[source])
/decl/observ/register(var/datum/event_source, var/datum/listener, var/proc_call)
. = ..()
if(.)
event_sources_count[event_source] += 1
event_listen_count[listener] += 1
/decl/observ/unregister(var/datum/event_source, var/datum/listener, var/proc_call)
. = ..()
if(.)
event_sources_count[event_source] -= 1
event_listen_count[listener] -= 1
/decl/observ/register_global(var/datum/listener, var/proc_call)
. = ..()
if(.)
global_listen_count[listener] += 1
/decl/observ/unregister_global(var/datum/listener, var/proc_call)
. = ..()
if(.)
global_listen_count[listener] -= 1
/decl/observ/destroyed/proc/cleanup_global_listener(listener, listen_count)
global_listen_count -= listener
for(var/entry in all_observable_events.events)
var/decl/observ/event = entry
if(event.unregister_global(listener))
log_debug("[event] - [listener] was deleted while still registered to global events.")
if(!(--listen_count))
return
/decl/observ/destroyed/proc/cleanup_source_listeners(event_source, source_listener_count)
event_sources_count -= event_source
for(var/entry in all_observable_events.events)
var/decl/observ/event = entry
var/proc_owners = event.event_sources[event_source]
if(proc_owners)
for(var/proc_owner in proc_owners)
if(event.unregister(event_source, proc_owner))
log_debug("[event] - [event_source] was deleted while still being listened to by [proc_owner].")
if(!(--source_listener_count))
return
/decl/observ/destroyed/proc/cleanup_event_listener(listener, listener_count)
event_listen_count -= listener
for(var/entry in all_observable_events.events)
var/decl/observ/event = entry
for(var/event_source in event.event_sources)
if(event.unregister(event_source, listener))
log_debug("[event] - [listener] was deleted while still listening to [event_source].")
if(!(--listener_count))
return