# # RocketMUD was written by Jon Lambert, 2006. # It is based on SocketMUD(tm) written by Brian Graversen. # This code is released to the public domain. # # # This file contains the event data struture, global variables # and specially defined values like MAX_EVENT_HASH. # # the event structure class Event attr_accessor :fun, :argument, :passes, :owner, :bucket, :ownertype, :type def initialize f, t @fun = f # the function being called @type = t # event type :event_xxx_yyy @argument = nil # the text argument given (if any) @owner = nil # this is the owner of the event, we # use a union to make sure any of the # types can be used for an event. @passes = 0 # how long before this event executes @bucket = 0 # which bucket is this event in @ownertype = :event_unowned # type of owner (unlinking req) end # function :: dequeue_event() # arguments :: the event to dequeue. # # This function takes an event which has _already_ been enqueued, and # removes it both from the event queue, and from the owners local list. # This function is usually called when the owner is destroyed or after # the event is executed. def dequeue_event # dequeue from the bucket $eventqueue[@bucket].delete self # dequeue from owners local list case @ownertype when :event_owner_game $global_events.delete self when :event_owner_dmob, :event_owner_dsocket @owner.events.delete self else bug "dequeue_event: event type %s has no owner.", @type end end # event_game_tick is just to show how to make global events # which can be used to update the game. # def event_game_tick # send a tick message to everyone $dmobile_list.each do |dMob| dMob.text_to_mobile "Tick!\r\n" end # enqueue another game tick in 10 minutes ev = Event.new :event_game_tick, :event_game_tick add_event_game ev, 10 * 60 * PULSES_PER_SECOND return false end def event_mobile_save # Check to see if there is an owner of this event. # If there is no owner, we return TRUE, because # it's the safest - and post a bug message. # dMob = @owner if dMob == nil bug "event_mobile_save: no owner." return true end # save the actual player file save_player dMob # enqueue a new event to save the pfile in 2 minutes ev = Event.new :event_mobile_save, :event_mobile_save dMob.add_event ev, 2 * 60 * PULSES_PER_SECOND return false end def event_socket_idle # Check to see if there is an owner of this event. # If there is no owner, we return TRUE, because # it's the safest - and post a bug message. # dSock = @owner if dSock == nil bug "event_socket_idle: no owner." return true end # tell the socket that it has idled out, and close it dSock.text_to_socket "You have idled out...\r\n\r\n" dSock.close_socket false # since we closed the socket, all events owned # by that socket has been dequeued, and we need # to return TRUE, so the caller knows this. # return true end end # function :: enqueue_event() # arguments :: the event to enqueue and the delay time. # ====================================================== # This function takes an event which has _already_ been # linked locally to it's owner, and places it in the # event queue, thus making it execute in the given time. def enqueue_event event, game_pulses # check to see if the event has been attached to an owner if event.ownertype == :event_unowned bug "enqueue_event: event type %d with no owner.", event.type return false end # An event must be enqueued into the future if game_pulses < 1 game_pulses = 1 end # calculate which bucket to put the event in, # and how many passes the event must stay in the queue. bucket = (game_pulses + $current_bucket) % MAX_EVENT_HASH passes = game_pulses / MAX_EVENT_HASH # let the event store this information event.passes = passes event.bucket = bucket # attach the event in the queue $eventqueue[bucket] << event # success return true end # function :: init_event_queue() # arguments :: what section to initialize. # # This function is used to initialize the event queue, and the first # section should be initialized at boot, the second section should be # called after all areas, players, monsters, etc has been loaded into # memory, and it should contain all maintanence events. def init_event_queue section if section == 1 $eventqueue = Array.new(MAX_EVENT_HASH) {Array.new} $global_events = [] $current_bucket = 0 elsif section == 2 event = Event.new :event_game_tick, :event_game_tick add_event_game event, 10 * 60 * PULSES_PER_SECOND end end # function :: heartbeat() # arguments :: none # ====================================================== # This function is called once per game pulse, and it will # check the queue, and execute any pending events, which # has been enqueued to execute at this specific time. def heartbeat # current_bucket should be global, it is also used in enqueue_event # to figure out what bucket to place the new event in. $current_bucket = ($current_bucket + 1) % MAX_EVENT_HASH $eventqueue[$current_bucket].each do |event| # Here we use the event->passes integer, to keep track of # how many times we have ignored this event. if event.passes > 0 event.passes -= 1 next end # execute event and extract if needed. We assume that all # event functions are of the following prototype # # bool event_function ( EVENT_DATA *event ); # # Any event returning TRUE is not dequeued, it is assumed # that the event has dequeued itself. ret = event.send event.fun if !ret event.dequeue_event end end end # function :: add_event_game() # arguments :: the event and the delay # ====================================================== # This funtion attaches an event to the list og game # events, and makes sure it's enqueued with the correct # delay time. def add_event_game event, delay # check to see if the event has a type if event.type == :event_none bug "add_event_game: no type." return end # check to see of the event has a callback function if event.fun == nil bug "add_event_game: event type %d has no callback function.", event.type return end # set the correct variables for this event event.ownertype = :event_owner_game # attach the event to the gamelist $global_events << event # attempt to enqueue the event if enqueue_event(event, delay) == false bug "add_event_game: event type %d failed to be enqueued.", event.type end end # function :: init_events_mobile() # arguments :: the mobile # ====================================================== # this function should be called when a player is loaded, # it will initialize all updating events for that player. def init_events_player dMob # save the player every 2 minutes event = Event.new :event_mobile_save, :event_mobile_save dMob.add_event event, 2 * 60 * PULSES_PER_SECOND end # function :: init_events_socket() # arguments :: the mobile # ====================================================== # this function should be called when a socket connects, # it will initialize all updating events for that socket. def init_events_socket dSock # disconnect/idle event = Event.new :event_socket_idle, :event_socket_idle dSock.add_event event, 5 * 60 * PULSES_PER_SECOND end