Module: Lich::DragonRealms::CustomSubstitutions Private

Defined in:
documented/dragonrealms/custom_substitutions.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Merges a player's own substitution/normalization entries (from their character settings) on top of core Lich's built-in defaults, so a player can teach Lich about their own problematic scrolls, creatures, boxes, etc. without waiting for a Lich release.

Core Lich keeps the authoritative default lists as frozen constants; those are passed in as defaults and are also the fallback when no dr-scripts / no user additions are present. Only the user's additions are read from settings and validated here -- defaults are trusted.

Every user entry is validated before it is merged. Invalid entries are dropped individually (lenient per-entry: one bad entry never disables the rest) and each rejection is reported to the player through Messaging with the exact key, index, offending value, reason, and consequence, so they understand precisely what happened and why.

Results are memoized per settings key (built once, on first use) so hot parse paths do not repeatedly call get_settings or recompile regexes. Call CustomSubstitutions.reset! to rebuild after a settings reload.

Examples:

Merge user scroll rewrites on top of the built-in defaults

pre = Lich::DragonRealms::CustomSubstitutions.resolve(
  :custom_scroll_substitutions,
  Lich::DragonRealms::DRC::DEFAULT_SCROLL_SUBSTITUTIONS_PRE,
  type: :pairs
)
pre.reduce(entry) { |text, (from, to)| text.sub(from, to) }

See Also:

Constant Summary collapse

MESSAGE_PREFIX =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Prefix on every player-facing message so the source is unambiguous.

'[CustomSubstitutions]'
REGEX_TIMEOUT_SECONDS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Per-regex evaluation budget (seconds) applied to every user-supplied pattern, so a pathological (catastrophic-backtracking) pattern raises Regexp::TimeoutError instead of hanging Lich. See apply_regexes.

1.0
SUPPORTED_TYPES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Supported validation shapes, dispatched on by resolve and validate_entry:

  • :pairs -- [from, to] literal String substitution pairs
  • :names -- bare canonical-name Strings
  • :regexes -- regular-expression Strings (or pre-compiled Regexps)
%i[pairs names regexes].freeze

Class Method Summary collapse

Class Method Details

.apply_regexes(text, patterns) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Folds patterns over text as successive String#sub(pattern, '') deletions, guarding each against Regexp::TimeoutError. A pattern that times out is skipped for this input and reported once (deduplicated by pattern source), never hanging the caller.

Examples:

apply_regexes('a gaudy scroll bedizened with gems', patterns)

Parameters:

  • text (String)

    the text to strip

  • patterns (Array<Regexp>)

    validated, timeout-bounded patterns (typically from resolve with type: :regexes)

Returns:

  • (String)

    text with every applicable pattern removed

See Also:



114
115
116
117
118
119
120
121
# File 'documented/dragonrealms/custom_substitutions.rb', line 114

def apply_regexes(text, patterns)
  patterns.reduce(text) do |current, pattern|
    current.sub(pattern, '')
  rescue Regexp::TimeoutError
    report_timeout(pattern)
    current
  end
end

.reset!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Clears the memoized merged lists (and the per-pattern timeout-report dedup set) so the next resolve/apply_regexes call re-reads settings and re-validates. Call this whenever settings are reloaded.

See Also:

  • #resolve


95
96
97
98
99
100
# File 'documented/dragonrealms/custom_substitutions.rb', line 95

def reset!
  @lock.synchronize do
    @cache = {}
    @reported_timeouts = []
  end
end

.resolve(key, defaults, type:) ⇒ Array

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns defaults merged with the validated user additions found at key in the player's settings, deduplicated and memoized.

Examples:

resolve(:custom_creature_normalizations, DEFAULTS, type: :names)

Parameters:

  • key (Symbol, String)

    the settings key holding user additions (e.g. :custom_scroll_substitutions)

  • defaults (Array)

    the trusted built-in default list (frozen constant); used as-is and returned unchanged when there are no valid additions

  • type (Symbol)

    one of SUPPORTED_TYPES; selects the validator

Returns:

  • (Array)

    defaults followed by the valid, deduplicated additions

Raises:

See Also:

  • #reset!


81
82
83
84
85
86
87
# File 'documented/dragonrealms/custom_substitutions.rb', line 81

def resolve(key, defaults, type:)
  raise ArgumentError, "unsupported type #{type.inspect}" unless SUPPORTED_TYPES.include?(type)

  # Double-checked: no lock on the warm path (the common case on hot
  # parse paths), lock only to populate a missing key.
  @cache[key] || @lock.synchronize { @cache[key] ||= (Array(defaults) + validated_additions(key, type)).uniq }
end