-- chat_utility - Shared functions and data for chat system -- (component of Chat System) -- Template attachment: none -- Include script when needed for chat system interaction -- Created by: Patrick Ferland -- Created on: 05/27/2008 -- Revision History -- 05/27/2008 P.Ferland - New script -- 06/02/2008 P.Ferland - Cleanup and documentation -- 06/10/2008 P.Ferland - Corrected attach/detach bugs -- 06/13/2008 P.Ferland - Removed spurious debugs -- 06/20/2008 P.Ferland - Added sender data replacement {.field_name} to channel decoding -- 06/23/2008 P.Ferland - Added trigger reference counting as core script -- 06/25/2008 P.Ferland - Removed trigger refcount -- 06/26/2008 P.Ferland - Added HTML-entity escaping of pipe to prevent breaking Metatags -- 07/11/2008 P.Ferland - Converted script registration to work at the template level -- 07/15/2008 P.Ferland - Added DENIED status code -- 07/22/2008 P.Ferland - Fixed broken script unregister -- 07/29/2008 P.Ferland - Added standard "system chat format" function -- 07/31/2008 P.Ferland - 0 radius makes spatial global -- 09/02/2008 P.Ferland - Added MAX_SEND_BUFFER_SIZE for "Stacking" commands like help, who, etc -- 09/08/2008 P.Ferland - Added fallback for system chat color in message formatting to prevent install-time errors -- 10/28/2008 P.Ferland - Replaced DoCommand with stylesheet.* for Attach/Detach Template Scripts -- Constants PLAYER_CHAT_SCRIPT = '11302:10' CHAT_ENTITY = '11302:11' MAX_SEND_BUFFER_SIZE = 500 -- Status codes to return in a chat_entity callback CHAT_STATUS_CODES = { UNKNOWN = -1, OK = 0, MODULE_NOT_FOUND = 1, SYNTAX_ERROR = 2, CHANNEL_NOT_FOUND = 3, PROPERTY_NOT_FOUND = 4, INVALID_ARGUMENT_VALUE = 5, DENIED = 6 } -- Script Properties Define Properties() -- Exported "library" functions ExportFunction('chat_system_channel_name') ExportFunction('chat_system_set_callback') ExportFunction('chat_system_strip_html') ExportFunction('chat_system_send_to_subscribers') ExportFunction('chat_system_send_to_subscribers_radial') ExportFunction('chat_system_log') ExportFunction('chat_system_debug') ExportFunction('chat_system_register_player_script') ExportFunction('chat_system_unregister_player_script') ExportFunction('chat_system_selectable_channel_decode') ExportFunction('chat_system_attach_core_player_scripts') ExportFunction('chat_system_format_system_message') -- Exported constants ExportConstant('CHAT_STATUS_CODES') ExportConstant('MAX_SEND_BUFFER_SIZE') end -- Commands -- no commands -- Triggers -- no triggers -- Local functions -- These functions are exported! -- Sets callback status, message, and data for a chat_entity function chat_system_set_callback(endpoint, status, status_text, data) local callback = endpoint.chat_system_callback_data if (callback ~= nil) then callback.status = status callback.status_text = status_text if (data == nil) then table.clear(callback.data) else callback.data = data end end end -- Returns the requested channel name as formatted for Chat System function chat_system_channel_name(entity_type, entity_name) local channel = {} -- NOTE: World should ALWAYS be available local w = GetWorld() if (w == nil) then Log(SEV_INFO, 'CHAT ERROR: Unable to identify world') return end channel.world = w.cleanName channel.qualifier = '/channel/' if (entity_type ~= nil) then if ( (entity_type == 'place') or (entity_type == 'spatial') ) then channel.qualifier = '#' elseif (entity_type == 'world') then channel.qualifier = '' else channel.qualifier = '/' .. entity_type .. '/' end end channel.entity = '' if (entity_name ~= nil) then if (entity_type == 'spatial') then channel.entity = entity_name .. '/_spatial_' elseif (entity_type ~= 'world') then channel.entity = entity_name end end return channel.world .. channel.qualifier .. channel.entity end -- Sends a given message to all subscribers in a channel function chat_system_send_to_subscribers(self, sender, channel, message, command) local endpoint = nil if (self.chat_system_subscriptions[channel] == nil) then -- chat_system_debug('Empty channel ' .. channel) else for _, endpoint in ipairs(self.chat_system_subscriptions[channel]) do SendTo(self, 'chat_system_send_message', 0, endpoint, message, channel, sender, command) end end end -- Sends a given message to all subscribers in a channel within the chat system's radial distance of the sender -- Only sends to/from physical entities function chat_system_send_to_subscribers_radial(self, sender, channel, message, command) if (self.chat_system_radius == 0) then chat_system_send_to_subscribers(self, sender, channel, message, command) return end local endpoint = nil local x = sender.x local y = sender.y if ( (x == nil) or (y == nil) ) then --chat_system_debug('cannot send radial as self is not physical') return -- Cannot send radial message if origination point is unknown end if (self.chat_system_subscriptions[channel] == nil) then SendTo(self, 'chat_system_debug', 0, 'Empty channel ' .. channel) else local endpoint = nil for _, endpoint in ipairs(self.chat_system_subscriptions[channel]) do local ex = endpoint.x local ey = endpoint.y if ( (ex ~= nil) and (ey ~= nil) ) then -- radial message only goes to entities with physical location local entity_name = endpoint.name if (entity_name == nil) then entity_name = 'entity' end local dx = ex - x local dy = ey - y if ( (dx * dx) + (dy * dy) <= (self.chat_system_radius * self.chat_system_radius) ) then SendTo(self, 'chat_system_send_message', 0, endpoint, message, channel, sender, command) end end end end end -- Escapes HTML in chat function chat_system_strip_html(message) return string.gsub(string.gsub(string.gsub(string.gsub(message, "\038", "\038amp;"), '>', "\038gt;"), '<', "\038lt;"), '|', "\038#124;") end -- Writes a message to the event log with CHAT SYSTEM prefix function chat_system_log(message) Log(SEV_INFO, 'CHAT SYSTEM: ' .. message) end -- Sends a message to client debugging with CHAT SYSTEM prefix function chat_system_debug(message) Debug('CHAT SYSTEM: ' .. message) end -- Registers a script for attachment to players through Chat System function chat_system_register_player_script(self, script_id) chat_system_log('Registering player script ' .. script_id) if (self.chat_system_plugin_player_scripts == nil) then chat_system_log('Could not register script!') return end self.chat_system_plugin_player_scripts.detached[script_id] = nil self.chat_system_plugin_player_scripts.attached[script_id] = script_id chat_system_attach_template_script('0:1', script_id) chat_system_log('Registration complete for ' .. script_id) end -- Registers a script for detachment from players through Chat System function chat_system_unregister_player_script(self, script_id) chat_system_log('Unregistering player script ' .. script_id) if (self.chat_system_plugin_player_scripts == nil) then chat_system_log('Could not unregister script!') return end self.chat_system_plugin_player_scripts.detached[script_id] = script_id self.chat_system_plugin_player_scripts.attached[script_id] = nil chat_system_detach_template_script('0:1', script_id) chat_system_log('Unregistration complete for ' .. script_id) end -- Translates placeholder in "selectable channel" definitions into a proper channel name function chat_system_selectable_channel_decode(self, sender, selectable_channel) local decode_string = self.chat_system_selectable_channels[selectable_channel] if (decode_string == nil) then return '' end local w = GetWorld() local p = GetPlace() decode_string = string.gsub(decode_string, '{world}', w.cleanName) decode_string = string.gsub(decode_string, '{place}', p.name) -- Custom placeholder fields: replace {.field_name} with sender.field_name value local sender_data_replacement = '' for sender_data_replacement in string.gmatch(decode_string, "{%.[_*%w*]*}") do local field_name = string.gsub(string.gsub(string.gsub(sender_data_replacement, '{', ''), '}', ''), "%.", '') local data = sender[field_name] if (data == nil) then data = 'UNKNOWN' end decode_string = string.gsub(decode_string, sender_data_replacement, data) end return decode_string end function chat_system_attach_core_player_scripts(player) chat_system_debug('chat_system_attach_core_player_scripts called (DEPRECATED -- remove reference)') -- Special chat system scripts if (HasScriptById(player, CHAT_ENTITY) == 0) then AttachScriptById(player, CHAT_ENTITY) end if (HasScriptById(player, PLAYER_CHAT_SCRIPT) == 0) then AttachScriptById(player, PLAYER_CHAT_SCRIPT) end end function chat_system_attach_template_script(template_id, script_id) stylesheet.AttachTemplateScript(template_id, script_id) end function chat_system_detach_template_script(template_id, script_id) stylesheet.DetachTemplateScript(template_id, script_id) end function chat_system_format_system_message(text) local cs = chat_system_singleton() local system_color = '#AB9A89' if (cs ~= nil) then system_color = cs.chat_system_system_color end return '' .. text .. '' end function chat_system_singleton() local w = GetWorld() local cs = w.chat_system return cs end