From 1bfc6e6929f0976e250844b1e3a68d76c7945c16 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Fri, 27 Sep 2019 12:37:16 -0500 Subject: [PATCH] Initial work on Scripting sandbox, no longer Entity's responsibility. --- assets/base/door/scripts/door.rb | 6 ++-- i-mic-fps.rb | 5 ++- lib/game_objects/entity.rb | 12 ++----- lib/scripting.rb | 3 +- lib/scripting/sandbox.rb | 25 ++++++++++++++ lib/scripting/whitelist.rb | 59 ++++++++++++++++++++++++++++++++ lib/subscription.rb | 4 +-- 7 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 lib/scripting/sandbox.rb create mode 100644 lib/scripting/whitelist.rb diff --git a/assets/base/door/scripts/door.rb b/assets/base/door/scripts/door.rb index 225be42..0ca54c5 100644 --- a/assets/base/door/scripts/door.rb +++ b/assets/base/door/scripts/door.rb @@ -1,9 +1,9 @@ -origin = self.position +origin = entity.position on.entity_moved do |event| if origin.distance3d(event.entity.position) <= 3.0 - self.position = origin + Vector.up * 2.4 + entity.position = origin + Vector.up * 2.4 else - self.position = origin + entity.position = origin end end \ No newline at end of file diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 1cd8dcb..97fa7b4 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -56,7 +56,6 @@ require_relative "lib/ui/menus/main_menu" require_relative "lib/states/game_states/game" require_relative "lib/states/game_states/loading_state" -require_relative "lib/scripting" require_relative "lib/subscription" require_relative "lib/publisher" require_relative "lib/event" @@ -65,6 +64,10 @@ require_relative "lib/event_handlers/input" require_relative "lib/event_handlers/entity_moved" require_relative "lib/event_handlers/entity_lifecycle" +require_relative "lib/scripting" +require_relative "lib/scripting/sandbox" +require_relative "lib/scripting/whitelist" + require_relative "lib/component" require_relative "lib/components/building" diff --git a/lib/game_objects/entity.rb b/lib/game_objects/entity.rb index 0cbf4ea..c06732c 100644 --- a/lib/game_objects/entity.rb +++ b/lib/game_objects/entity.rb @@ -4,7 +4,6 @@ class IMICFPS # A game object is any renderable thing class Entity include CommonMethods - include Scripting attr_accessor :scale, :visible, :renderable, :backface_culling attr_accessor :position, :orientation, :velocity @@ -38,6 +37,7 @@ class IMICFPS @last_position = Vector.new(@position.x, @position.y, @position.z) + @sandboxes = [] load_scripts setup @@ -56,15 +56,7 @@ class IMICFPS def load_scripts @manifest.scripts.each do |script| - instance_eval(script.source) - end - end - - def method_missing(method, *args, &block) - unless component = Component.get(method) - raise NoMemoryError, "undefined method '#{method}' for #<#{self.class}:#{self.object_id}>" - else - return component + @sandboxes << Scripting::SandBox.new(entity: self, script: script) end end diff --git a/lib/scripting.rb b/lib/scripting.rb index 9a49e40..de7d6ba 100644 --- a/lib/scripting.rb +++ b/lib/scripting.rb @@ -1,7 +1,8 @@ class IMICFPS module Scripting def on - Subscription.new(self) + # self is a Scripting::SandBox + Subscription.new(self.entity) end def component(name) diff --git a/lib/scripting/sandbox.rb b/lib/scripting/sandbox.rb new file mode 100644 index 0000000..471bc5b --- /dev/null +++ b/lib/scripting/sandbox.rb @@ -0,0 +1,25 @@ +class IMICFPS + module Scripting + class SandBox + include Scripting + def initialize(entity:, script:) + @entity = entity + @script = script.name + + execute(script.source) if source_safe?(script.source) + end + + def source_safe?(source) + true # TODO: implement whitelisting/safety checks + end + + def execute(source) + instance_eval(source) + end + + def entity + @entity + end + end + end +end \ No newline at end of file diff --git a/lib/scripting/whitelist.rb b/lib/scripting/whitelist.rb new file mode 100644 index 0000000..f5c78db --- /dev/null +++ b/lib/scripting/whitelist.rb @@ -0,0 +1,59 @@ +class IMICFPS + module Scripting + CONSTANTS_WHITELIST = [ + # Basic types + Array, Hash, String, Math, Time, Struct, Integer, Float, Numeric, Range, Proc, + # Ruby information + RUBY_VERSION, RUBY_ENGINE, + # Local + Vector, BoundingBox, Ray, + ] + + METHOD_WHITELIST = [ + Subscription.subscribable_events, + :|, :<, :<<, :>, :>>, :<=, :>=, :<=>, :==, :===, :=~, :[], :[]=, + # Maths + :+, :%, :*, :/, :-, :&, :**, + :acos, :acosh, :asin, :asinh, :atan, :atan2, :atanh, :cbrt, :cos, :cosh, + :erf, :erfc, :exp, :frexp, :gamma, :hypot, :ldexp, :lgamma, :log, :log10, :log2, + :sin, :sinh, :sqrt, :tan, :tanh, + # Keywords + :do, :if, :else, :elsif, :case, :when, :and, :or, + # Object + :new, :class, :name, :clone, :dup, + # Proc + :call, + # Time + :at, :now, :utc, :gm, :local, :mktime, + # Printing + :inspect, :puts, :print, :console, :stdin, + # Querying + :between?, :block_given?, :is_a?, :kind_of?, :instance_of?, :empty?, :respond_to?, :equal?, + # String + :ascii_only?, :b, :bytes, :bytesize, :byteslice, :capitalize, :capitalize!, :casecmp, :casecmp?, :center, :chars, + :chomp, :chomp!, :chop, :chop!, :chr, :clear, :codepoints, :concat, :count, :crypt, :delete, :delete!, :delete_prefix, + :delete_prefix!, :delete_suffix, :delete_suffix!, :downcase, :downcase!, :dump, :each_byte, :each_char, :each_codepoint, + :each_grapheme_cluster, :each_line, :empty?, :encode, :encode!, :encoding, :end_with?, :eql?, :force_encoding, :freeze, :getbyte, + :grapheme_clusters, :gsub, :gsub!, :hash, :hex, :include?, :index, :insert, :inspect, :intern, :length, :lines, :ljust, :lstrip, + :lstrip!, :match, :match?, :next, :next!, :oct, :ord, :partition, :prepend, :replace, :reverse, :reverse!, :rindex, :rjust, :rpartition, + :rstrip, :rstrip!, :scan, :scrub, :scrub!, :setbyte, :size, :slice, :slice!, :split, :squeeze, :squeeze!, :start_with?, :strip, :strip!, + :sub, :sub!, :succ, :succ!, :sum, :swapcase, :swapcase!, :tr, :tr!, :tr_s, :tr_s!, :undump, :unicode_normalize, :unicode_normalize!, + :unicode_normalized?, :unpack, :unpack1, :upcase, :upcase!, :upto, :valid_encoding?, + # Array/Hash/Enumerable + :all?, :any?, :append, :assoc, :at, :bsearch, :bsearch_index, :chain, :chunk, :chunk_while, :clear, :collect, :collect!, :collect_concat, + :combination, :compact, :compact!, :compare_by_identity, :compare_by_identity?, :concat, :count, :cycle, :default, :default=, :default_proc, + :default_proc=, :delete, :delete_at, :delete_if, :detect, :difference, :dig, :drop, :drop_while, :each, :each_cons, :each_entry, :each_index, + :each_key, :each_pair, :each_slice, :each_value, :each_with_index, :each_with_object, :empty?, :entries, :eql?, :fetch, :fetch_values, :fill, + :filter, :filter!, :find, :find_all, :find_index, :first, :flat_map, :flatten, :flatten!, :grep, :grep_v, :group_by, :has_key?, :has_value?, + :hash, :include?, :index, :inject, :insert, :inspect, :invert, :join, :keep_if, :key, :key?, :keys, :last, :lazy, :length, :map, :map!, + :max, :max_by, :member?, :merge, :merge!, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :pack, :partition, :permutation, :pop, :prepend, + :product, :push, :rassoc, :reduce, :rehash, :reject, :reject!, :repeated_combination, :repeated_permutation, :replace, :reverse, :reverse!, + :reverse_each, :rindex, :rotate, :rotate!, :sample, :select, :select!, :shift, :shuffle, :shuffle!, :size, :slice, :slice!, :slice_after, :slice_before, + :slice_when, :sort, :sort!, :sort_by, :sort_by!, :store, :sum, :take, :take_while, :to_a, :to_ary, :to_h, :to_hash, :to_proc, :to_s, :to_set, + :transform_keys, :transform_keys!, :transform_values, :transform_values!, :transpose, :union, :uniq, :uniq!, :unshift, :update, :value?, :values, + :values_at, :zip, + # Casting + :to_c, :to_f, :to_i, :to_r, :to_str, :to_ary, :to_h, :to_hash, :to_proc, :to_a, :to_s, :to_sym, + ].flatten + end +end \ No newline at end of file diff --git a/lib/subscription.rb b/lib/subscription.rb index ff03b26..c83dfd7 100644 --- a/lib/subscription.rb +++ b/lib/subscription.rb @@ -10,7 +10,7 @@ class IMICFPS end def method_missing(event, *args, &block) - return unless subscribable_events.include?(event) + return unless Subscription.subscribable_events.include?(event) @event, @args, @block = event, args, block Publisher.subscribe(self) @@ -22,7 +22,7 @@ class IMICFPS end end - private def subscribable_events + def self.subscribable_events [ :tick, :create, :move, :destroy,